diff --git a/.gitmodules b/.gitmodules index e0cfc0869d05..cbe82c215f7c 100755 --- a/.gitmodules +++ b/.gitmodules @@ -35,30 +35,12 @@ [submodule "src/sonic-utilities"] path = src/sonic-utilities url = https://github.com/Azure/sonic-utilities -[submodule "platform/broadcom/sonic-platform-modules-s6000"] - path = platform/broadcom/sonic-platform-modules-s6000 - url = https://github.com/Azure/sonic-platform-modules-s6000 -[submodule "platform/broadcom/sonic-platform-modules-arista"] - path = platform/broadcom/sonic-platform-modules-arista - url = https://github.com/aristanetworks/sonic -[submodule "platform/broadcom/sonic-platform-modules-dell"] - path = platform/broadcom/sonic-platform-modules-dell - url = https://github.com/Azure/sonic-platform-modules-dell -[submodule "platform/broadcom/sonic-platform-modules-ingrasys"] - path = platform/broadcom/sonic-platform-modules-ingrasys - url = https://github.com/Ingrasys-sonic/sonic-platform-modules-ingrasys [submodule "src/sonic-platform-common"] path = src/sonic-platform-common url = https://github.com/Azure/sonic-platform-common [submodule "src/sonic-platform-daemons"] path = src/sonic-platform-daemons url = https://github.com/Azure/sonic-platform-daemons -[submodule "platform/broadcom/sonic-platform-modules-accton"] - path = platform/broadcom/sonic-platform-modules-accton - url = https://github.com/edge-core/sonic-platform-modules-accton.git -[submodule "platform/broadcom/sonic-platform-modules-cel"] - path = platform/broadcom/sonic-platform-modules-cel - url = https://github.com/celestica-Inc/sonic-platform-modules-cel.git [submodule "src/sonic-frr/frr"] path = src/sonic-frr/frr url = https://github.com/FRRouting/frr.git @@ -68,12 +50,3 @@ [submodule "platform/p4/SAI-P4-BM"] path = platform/p4/SAI-P4-BM url = https://github.com/Mellanox/SAI-P4-BM.git -[submodule "platform/nephos/sonic-platform-modules-ingrasys"] - path = platform/nephos/sonic-platform-modules-ingrasys - url = https://github.com/Ingrasys-sonic/sonic-platform-modules-ingrasys-nephos.git -[submodule "platform/broadcom/sonic-platform-modules-quanta"] - path = platform/broadcom/sonic-platform-modules-quanta - url = https://github.com/QuantaSwitchONIE/sonic-platform-modules-quanta.git -[submodule "platform/broadcom/sonic-platform-modules-mitac"] - path = platform/broadcom/sonic-platform-modules-mitac - url = https://github.com/MiTAC-EBU/sonic-platform-modules-mitac.git diff --git a/platform/broadcom/sonic-platform-modules-accton b/platform/broadcom/sonic-platform-modules-accton deleted file mode 160000 index 406c4e675ea7..000000000000 --- a/platform/broadcom/sonic-platform-modules-accton +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 406c4e675ea73d52827b520958c9956551a94e8e diff --git a/platform/broadcom/sonic-platform-modules-accton/.gitignore b/platform/broadcom/sonic-platform-modules-accton/.gitignore new file mode 100644 index 000000000000..f805e810e5c6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/.gitignore @@ -0,0 +1,33 @@ +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su diff --git a/platform/broadcom/sonic-platform-modules-accton/LICENSE b/platform/broadcom/sonic-platform-modules-accton/LICENSE new file mode 100644 index 000000000000..bc693f7a4c40 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/LICENSE @@ -0,0 +1,16 @@ +Copyright (C) 2016 Microsoft, Inc +Copyright (C) 2017 Accton Technology Corporation + +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. diff --git a/platform/broadcom/sonic-platform-modules-accton/README.md b/platform/broadcom/sonic-platform-modules-accton/README.md new file mode 100644 index 000000000000..0ff20bb2d21b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/README.md @@ -0,0 +1 @@ +platform drivers of Accton products for the SONiC project diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/classes/__init__.py b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/classes/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/classes/fanutil.py b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/classes/fanutil.py new file mode 100755 index 000000000000..c16771e214e4 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/classes/fanutil.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# +# ------------------------------------------------------------------ + +try: + import time + import logging + from collections import namedtuple +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + + +class FanUtil(object): + """Platform-specific FanUtil class""" + + FAN_NUM_ON_MAIN_BROAD = 5 + FAN_NUM_1_IDX = 1 + FAN_NUM_2_IDX = 2 + FAN_NUM_3_IDX = 3 + FAN_NUM_4_IDX = 4 + FAN_NUM_5_IDX = 5 + + FAN_NODE_NUM_OF_MAP = 6 + FAN_NODE_FAULT_IDX_OF_MAP = 1 + FAN_NODE_SPEED_IDX_OF_MAP = 2 + FAN_NODE_DIR_IDX_OF_MAP = 3 + FAN_NODE_DUTY_IDX_OF_MAP = 4 + FANR_NODE_FAULT_IDX_OF_MAP = 5 + FANR_NODE_SPEED_IDX_OF_MAP = 6 + + BASE_VAL_PATH = '/sys/devices/platform/as5712_54x_fan/{0}' + + #logfile = '' + #loglevel = logging.INFO + + """ Dictionary where + key1 = fan id index (integer) starting from 1 + key2 = fan node index (interger) starting from 1 + value = path to fan device file (string) """ + _fan_to_device_path_mapping = {} + + _fan_to_device_node_mapping = { + (FAN_NUM_1_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan1_fault', + (FAN_NUM_1_IDX, FAN_NODE_SPEED_IDX_OF_MAP): 'fan1_speed_rpm', + (FAN_NUM_1_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan1_direction', + (FAN_NUM_1_IDX, FAN_NODE_DUTY_IDX_OF_MAP): 'fan1_duty_cycle_percentage', + (FAN_NUM_1_IDX, FANR_NODE_FAULT_IDX_OF_MAP): 'fanr1_fault', + (FAN_NUM_1_IDX, FANR_NODE_SPEED_IDX_OF_MAP): 'fanr1_speed_rpm', + + (FAN_NUM_2_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan2_fault', + (FAN_NUM_2_IDX, FAN_NODE_SPEED_IDX_OF_MAP): 'fan2_speed_rpm', + (FAN_NUM_2_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan2_direction', + (FAN_NUM_2_IDX, FAN_NODE_DUTY_IDX_OF_MAP): 'fan2_duty_cycle_percentage', + (FAN_NUM_2_IDX, FANR_NODE_FAULT_IDX_OF_MAP): 'fanr2_fault', + (FAN_NUM_2_IDX, FANR_NODE_SPEED_IDX_OF_MAP): 'fanr2_speed_rpm', + + (FAN_NUM_3_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan3_fault', + (FAN_NUM_3_IDX, FAN_NODE_SPEED_IDX_OF_MAP): 'fan3_speed_rpm', + (FAN_NUM_3_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan3_direction', + (FAN_NUM_3_IDX, FAN_NODE_DUTY_IDX_OF_MAP): 'fan3_duty_cycle_percentage', + (FAN_NUM_3_IDX, FANR_NODE_FAULT_IDX_OF_MAP): 'fanr3_fault', + (FAN_NUM_3_IDX, FANR_NODE_SPEED_IDX_OF_MAP): 'fanr3_speed_rpm', + + (FAN_NUM_4_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan4_fault', + (FAN_NUM_4_IDX, FAN_NODE_SPEED_IDX_OF_MAP): 'fan4_speed_rpm', + (FAN_NUM_4_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan4_direction', + (FAN_NUM_4_IDX, FAN_NODE_DUTY_IDX_OF_MAP): 'fan4_duty_cycle_percentage', + (FAN_NUM_4_IDX, FANR_NODE_FAULT_IDX_OF_MAP): 'fanr4_fault', + (FAN_NUM_4_IDX, FANR_NODE_SPEED_IDX_OF_MAP): 'fanr4_speed_rpm', + + (FAN_NUM_5_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan5_fault', + (FAN_NUM_5_IDX, FAN_NODE_SPEED_IDX_OF_MAP): 'fan5_speed_rpm', + (FAN_NUM_5_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan5_direction', + (FAN_NUM_5_IDX, FAN_NODE_DUTY_IDX_OF_MAP): 'fan5_duty_cycle_percentage', + (FAN_NUM_5_IDX, FANR_NODE_FAULT_IDX_OF_MAP): 'fanr5_fault', + (FAN_NUM_5_IDX, FANR_NODE_SPEED_IDX_OF_MAP): 'fanr5_speed_rpm', + } + + def _get_fan_to_device_node(self, fan_num, node_num): + return self._fan_to_device_node_mapping[(fan_num, node_num)] + + def _get_fan_node_val(self, fan_num, node_num): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num:%d', fan_num) + return None + + if node_num < self.FAN_NODE_FAULT_IDX_OF_MAP or node_num > self.FAN_NODE_NUM_OF_MAP: + logging.debug('GET. Parameter error. node_num:%d', node_num) + return None + + device_path = self.get_fan_to_device_path(fan_num, node_num) + try: + val_file = open(device_path, 'r') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + content = val_file.readline().rstrip() + + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return int(content) + + def _set_fan_node_val(self, fan_num, node_num, val): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num:%d', fan_num) + return None + + if node_num < self.FAN_NODE_FAULT_IDX_OF_MAP or node_num > self.FAN_NODE_NUM_OF_MAP: + logging.debug('GET. Parameter error. node_num:%d', node_num) + return None + + content = str(val) + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + device_path = self.get_fan_to_device_path(fan_num, node_num) + try: + val_file = open(device_path, 'w') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + val_file.write(content) + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return True + + def __init__(self): + fan_path = self.BASE_VAL_PATH + + for fan_num in range(self.FAN_NUM_1_IDX, self.FAN_NUM_ON_MAIN_BROAD+1): + for node_num in range(self.FAN_NODE_FAULT_IDX_OF_MAP, self.FAN_NODE_NUM_OF_MAP+1): + self._fan_to_device_path_mapping[(fan_num, node_num)] = fan_path.format( + self._fan_to_device_node_mapping[(fan_num, node_num)]) + + def get_num_fans(self): + return self.FAN_NUM_ON_MAIN_BROAD + + def get_idx_fan_start(self): + return self.FAN_NUM_1_IDX + + def get_num_nodes(self): + return self.FAN_NODE_NUM_OF_MAP + + def get_idx_node_start(self): + return self.FAN_NODE_FAULT_IDX_OF_MAP + + def get_size_node_map(self): + return len(self._fan_to_device_node_mapping) + + def get_size_path_map(self): + return len(self._fan_to_device_path_mapping) + + def get_fan_to_device_path(self, fan_num, node_num): + return self._fan_to_device_path_mapping[(fan_num, node_num)] + + def get_fan_fault(self, fan_num): + return self._get_fan_node_val(fan_num, self.FAN_NODE_FAULT_IDX_OF_MAP) + + def get_fan_speed(self, fan_num): + return self._get_fan_node_val(fan_num, self.FAN_NODE_SPEED_IDX_OF_MAP) + + def get_fan_dir(self, fan_num): + return self._get_fan_node_val(fan_num, self.FAN_NODE_DIR_IDX_OF_MAP) + + def get_fan_duty_cycle(self, fan_num): + return self._get_fan_node_val(fan_num, self.FAN_NODE_DUTY_IDX_OF_MAP) + + def set_fan_duty_cycle(self, fan_num, val): + return self._set_fan_node_val(fan_num, self.FAN_NODE_DUTY_IDX_OF_MAP, val) + + def get_fanr_fault(self, fan_num): + return self._get_fan_node_val(fan_num, self.FANR_NODE_FAULT_IDX_OF_MAP) + + def get_fanr_speed(self, fan_num): + return self._get_fan_node_val(fan_num, self.FANR_NODE_SPEED_IDX_OF_MAP) + + def get_fan_status(self, fan_num): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num, %d', fan_num) + return None + + if self.get_fan_fault(fan_num) is not None and self.get_fan_fault(fan_num) > 0: + logging.debug('GET. FAN fault. fan_num, %d', fan_num) + return False + + if self.get_fanr_fault(fan_num) is not None and self.get_fanr_fault(fan_num) > 0: + logging.debug('GET. FANR fault. fan_num, %d', fan_num) + return False + + return True + +#def main(): +# fan = FanUtil() +# +# print 'get_size_node_map : %d' % fan.get_size_node_map() +# print 'get_size_path_map : %d' % fan.get_size_path_map() +# for x in range(fan.get_idx_fan_start(), fan.get_num_fans()+1): +# for y in range(fan.get_idx_node_start(), fan.get_num_nodes()+1): +# print fan.get_fan_to_device_path(x, y) +# +#if __name__ == '__main__': +# main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/classes/thermalutil.py b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/classes/thermalutil.py new file mode 100755 index 000000000000..5b8f890e8819 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/classes/thermalutil.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# +# ------------------------------------------------------------------ + +try: + import time + import logging + import glob + from collections import namedtuple +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + + +class ThermalUtil(object): + """Platform-specific ThermalUtil class""" + + THERMAL_NUM_ON_MAIN_BROAD = 3 + THERMAL_NUM_1_IDX = 1 # 1_ON_MAIN_BROAD + THERMAL_NUM_2_IDX = 2 # 2_ON_MAIN_BROAD + THERMAL_NUM_3_IDX = 3 # 3_ON_MAIN_BROAD + + BASE_VAL_PATH = '/sys/bus/i2c/devices/{0}-00{1}/hwmon/hwmon*/temp1_input' + + """ Dictionary where + key1 = thermal id index (integer) starting from 1 + value = path to fan device file (string) """ + _thermal_to_device_path_mapping = {} + + _thermal_to_device_node_mapping = { + THERMAL_NUM_1_IDX: ['61', '48'], + THERMAL_NUM_2_IDX: ['62', '49'], + THERMAL_NUM_3_IDX: ['63', '4a'], + } + + def __init__(self): + thermal_path = self.BASE_VAL_PATH + + for x in range(self.THERMAL_NUM_1_IDX, self.THERMAL_NUM_ON_MAIN_BROAD+1): + self._thermal_to_device_path_mapping[x] = thermal_path.format( + self._thermal_to_device_node_mapping[x][0], + self._thermal_to_device_node_mapping[x][1]) + + def _get_thermal_node_val(self, thermal_num): + if thermal_num < self.THERMAL_NUM_1_IDX or thermal_num > self.THERMAL_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. thermal_num, %d', thermal_num) + return None + + device_path = self.get_thermal_to_device_path(thermal_num) + for filename in glob.glob(device_path): + try: + val_file = open(filename, 'r') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + content = val_file.readline().rstrip() + + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return int(content) + + + def get_num_thermals(self): + return self.THERMAL_NUM_ON_MAIN_BROAD + + def get_idx_thermal_start(self): + return self.THERMAL_NUM_1_IDX + + def get_size_node_map(self): + return len(self._thermal_to_device_node_mapping) + + def get_size_path_map(self): + return len(self._thermal_to_device_path_mapping) + + def get_thermal_to_device_path(self, thermal_num): + return self._thermal_to_device_path_mapping[thermal_num] + + def get_thermal_1_val(self): + return self._get_thermal_node_val(self.THERMAL_NUM_1_IDX) + + def get_thermal_2_val(self): + return self._get_thermal_node_val(self.THERMAL_NUM_2_IDX) + +#def main(): +# thermal = ThermalUtil() +# +# print 'get_size_node_map : %d' % thermal.get_size_node_map() +# print 'get_size_path_map : %d' % thermal.get_size_path_map() +# for x in range(thermal.get_idx_thermal_start(), thermal.get_num_thermals()+1): +# print thermal.get_thermal_to_device_path(x) +# +#if __name__ == '__main__': +# main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/Makefile b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/Makefile new file mode 100755 index 000000000000..39fe78bf8cae --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/Makefile @@ -0,0 +1,17 @@ +ifneq ($(KERNELRELEASE),) +obj-m:= i2c-mux-accton_as5712_54x_cpld.o \ + accton_as5712_54x_fan.o leds-accton_as5712_54x.o accton_as5712_54x_psu.o \ + cpr_4011_4mxx.o ym2651y.o + +else +ifeq (,$(KERNEL_SRC)) +$(error KERNEL_SRC is not defined) +else +KERNELDIR:=$(KERNEL_SRC) +endif +PWD:=$(shell pwd) +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules +clean: + rm -rf *.o *.mod.o *.mod.o *.ko .*cmd .tmp_versions Module.markers Module.symvers modules.order +endif diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/accton_as5712_54x_fan.c b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/accton_as5712_54x_fan.c new file mode 100755 index 000000000000..db81a1a8ca97 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/accton_as5712_54x_fan.c @@ -0,0 +1,437 @@ +/* + * A hwmon driver for the Accton as5710 54x fan contrl + * + * Copyright (C) 2013 Accton Technology Corporation. + * Brandon Chuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FAN_MAX_NUMBER 5 +#define FAN_SPEED_CPLD_TO_RPM_STEP 150 +#define FAN_SPEED_PRECENT_TO_CPLD_STEP 5 +#define FAN_DUTY_CYCLE_MIN 0 /* 10% ??*/ +#define FAN_DUTY_CYCLE_MAX 100 /* 100% */ + +#define CPLD_REG_FAN_STATUS_OFFSET 0xC +#define CPLD_REG_FANR_STATUS_OFFSET 0x1F +#define CPLD_REG_FAN_DIRECTION_OFFSET 0x1E + +#define CPLD_FAN1_REG_SPEED_OFFSET 0x10 +#define CPLD_FAN2_REG_SPEED_OFFSET 0x11 +#define CPLD_FAN3_REG_SPEED_OFFSET 0x12 +#define CPLD_FAN4_REG_SPEED_OFFSET 0x13 +#define CPLD_FAN5_REG_SPEED_OFFSET 0x14 + +#define CPLD_FANR1_REG_SPEED_OFFSET 0x18 +#define CPLD_FANR2_REG_SPEED_OFFSET 0x19 +#define CPLD_FANR3_REG_SPEED_OFFSET 0x1A +#define CPLD_FANR4_REG_SPEED_OFFSET 0x1B +#define CPLD_FANR5_REG_SPEED_OFFSET 0x1C + +#define CPLD_REG_FAN_PWM_CYCLE_OFFSET 0xD + +#define CPLD_FAN1_INFO_BIT_MASK 0x1 +#define CPLD_FAN2_INFO_BIT_MASK 0x2 +#define CPLD_FAN3_INFO_BIT_MASK 0x4 +#define CPLD_FAN4_INFO_BIT_MASK 0x8 +#define CPLD_FAN5_INFO_BIT_MASK 0x10 + +#define PROJECT_NAME + +#define LOCAL_DEBUG 0 + +static struct accton_as5712_54x_fan *fan_data = NULL; + +struct accton_as5712_54x_fan { + struct platform_device *pdev; + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 status[FAN_MAX_NUMBER]; /* inner first fan status */ + u32 speed[FAN_MAX_NUMBER]; /* inner first fan speed */ + u8 direction[FAN_MAX_NUMBER]; /* reconrd the direction of inner first and second fans */ + u32 duty_cycle[FAN_MAX_NUMBER]; /* control the speed of inner first and second fans */ + u8 r_status[FAN_MAX_NUMBER]; /* inner second fan status */ + u32 r_speed[FAN_MAX_NUMBER]; /* inner second fan speed */ +}; + +/*******************/ +#define MAKE_FAN_MASK_OR_REG(name,type) \ + CPLD_FAN##type##1_##name, \ + CPLD_FAN##type##2_##name, \ + CPLD_FAN##type##3_##name, \ + CPLD_FAN##type##4_##name, \ + CPLD_FAN##type##5_##name, + +/* fan related data + */ +static const u8 fan_info_mask[] = { + MAKE_FAN_MASK_OR_REG(INFO_BIT_MASK,) +}; + +static const u8 fan_speed_reg[] = { + MAKE_FAN_MASK_OR_REG(REG_SPEED_OFFSET,) +}; + +static const u8 fanr_speed_reg[] = { + MAKE_FAN_MASK_OR_REG(REG_SPEED_OFFSET,R) +}; + +/*******************/ +#define DEF_FAN_SET(id) \ + FAN##id##_FAULT, \ + FAN##id##_SPEED, \ + FAN##id##_DUTY_CYCLE, \ + FAN##id##_DIRECTION, \ + FANR##id##_FAULT, \ + FANR##id##_SPEED, + +enum sysfs_fan_attributes { + DEF_FAN_SET(1) + DEF_FAN_SET(2) + DEF_FAN_SET(3) + DEF_FAN_SET(4) + DEF_FAN_SET(5) +}; +/*******************/ +static void accton_as5712_54x_fan_update_device(struct device *dev); +static int accton_as5712_54x_fan_read_value(u8 reg); +static int accton_as5712_54x_fan_write_value(u8 reg, u8 value); + +static ssize_t fan_set_duty_cycle(struct device *dev, + struct device_attribute *da,const char *buf, size_t count); +static ssize_t fan_show_value(struct device *dev, + struct device_attribute *da, char *buf); + +extern int as5712_54x_cpld_read(unsigned short cpld_addr, u8 reg); +extern int as5712_54x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + + +/*******************/ +#define _MAKE_SENSOR_DEVICE_ATTR(prj, id) \ + static SENSOR_DEVICE_ATTR(prj##fan##id##_fault, S_IRUGO, fan_show_value, NULL, FAN##id##_FAULT); \ + static SENSOR_DEVICE_ATTR(prj##fan##id##_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##id##_SPEED); \ + static SENSOR_DEVICE_ATTR(prj##fan##id##_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, \ + fan_set_duty_cycle, FAN##id##_DUTY_CYCLE); \ + static SENSOR_DEVICE_ATTR(prj##fan##id##_direction, S_IRUGO, fan_show_value, NULL, FAN##id##_DIRECTION); \ + static SENSOR_DEVICE_ATTR(prj##fanr##id##_fault, S_IRUGO, fan_show_value, NULL, FANR##id##_FAULT); \ + static SENSOR_DEVICE_ATTR(prj##fanr##id##_speed_rpm, S_IRUGO, fan_show_value, NULL, FANR##id##_SPEED); + +#define MAKE_SENSOR_DEVICE_ATTR(prj,id) _MAKE_SENSOR_DEVICE_ATTR(prj,id) + +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 1) +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 2) +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 3) +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 4) +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 5) +/*******************/ + +#define _MAKE_FAN_ATTR(prj, id) \ + &sensor_dev_attr_##prj##fan##id##_fault.dev_attr.attr, \ + &sensor_dev_attr_##prj##fan##id##_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_##prj##fan##id##_duty_cycle_percentage.dev_attr.attr,\ + &sensor_dev_attr_##prj##fan##id##_direction.dev_attr.attr, \ + &sensor_dev_attr_##prj##fanr##id##_fault.dev_attr.attr, \ + &sensor_dev_attr_##prj##fanr##id##_speed_rpm.dev_attr.attr, + +#define MAKE_FAN_ATTR(prj, id) _MAKE_FAN_ATTR(prj, id) + +static struct attribute *accton_as5712_54x_fan_attributes[] = { + /* fan related attributes */ + MAKE_FAN_ATTR(PROJECT_NAME,1) + MAKE_FAN_ATTR(PROJECT_NAME,2) + MAKE_FAN_ATTR(PROJECT_NAME,3) + MAKE_FAN_ATTR(PROJECT_NAME,4) + MAKE_FAN_ATTR(PROJECT_NAME,5) + NULL +}; +/*******************/ + +/* fan related functions + */ +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + ssize_t ret = 0; + int data_index, type_index; + + accton_as5712_54x_fan_update_device(dev); + + if (fan_data->valid == 0) { + return ret; + } + + type_index = attr->index%FAN2_FAULT; + data_index = attr->index/FAN2_FAULT; + + switch (type_index) { + case FAN1_FAULT: + ret = sprintf(buf, "%d\n", fan_data->status[data_index]); + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FAN1_SPEED: + ret = sprintf(buf, "%d\n", fan_data->speed[data_index]); + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FAN1_DUTY_CYCLE: + ret = sprintf(buf, "%d\n", fan_data->duty_cycle[data_index]); + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FAN1_DIRECTION: + ret = sprintf(buf, "%d\n", fan_data->direction[data_index]); /* presnet, need to modify*/ + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FANR1_FAULT: + ret = sprintf(buf, "%d\n", fan_data->r_status[data_index]); + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FANR1_SPEED: + ret = sprintf(buf, "%d\n", fan_data->r_speed[data_index]); + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + default: + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d] \n", __FUNCTION__, __LINE__); + break; + } + + return ret; +} +/*******************/ +static ssize_t fan_set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) { + + int error, value; + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < FAN_DUTY_CYCLE_MIN || value > FAN_DUTY_CYCLE_MAX) + return -EINVAL; + + accton_as5712_54x_fan_write_value(CPLD_REG_FAN_PWM_CYCLE_OFFSET, value/FAN_SPEED_PRECENT_TO_CPLD_STEP); + + fan_data->valid = 0; + + return count; +} + +static const struct attribute_group accton_as5712_54x_fan_group = { + .attrs = accton_as5712_54x_fan_attributes, +}; + +static int accton_as5712_54x_fan_read_value(u8 reg) +{ + return as5712_54x_cpld_read(0x60, reg); +} + +static int accton_as5712_54x_fan_write_value(u8 reg, u8 value) +{ + return as5712_54x_cpld_write(0x60, reg, value); +} + +static void accton_as5712_54x_fan_update_device(struct device *dev) +{ + int speed, r_speed, fault, r_fault, ctrl_speed, direction; + int i; + + mutex_lock(&fan_data->update_lock); + + if (LOCAL_DEBUG) + printk ("Starting accton_as5712_54x_fan update \n"); + + if (!(time_after(jiffies, fan_data->last_updated + HZ + HZ / 2) || !fan_data->valid)) { + /* do nothing */ + goto _exit; + } + + fan_data->valid = 0; + + if (LOCAL_DEBUG) + printk ("Starting accton_as5712_54x_fan update 2 \n"); + + fault = accton_as5712_54x_fan_read_value(CPLD_REG_FAN_STATUS_OFFSET); + r_fault = accton_as5712_54x_fan_read_value(CPLD_REG_FANR_STATUS_OFFSET); + direction = accton_as5712_54x_fan_read_value(CPLD_REG_FAN_DIRECTION_OFFSET); + ctrl_speed = accton_as5712_54x_fan_read_value(CPLD_REG_FAN_PWM_CYCLE_OFFSET); + + if ( (fault < 0) || (r_fault < 0) || (direction < 0) || (ctrl_speed < 0) ) + { + if (LOCAL_DEBUG) + printk ("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); + goto _exit; /* error */ + } + + if (LOCAL_DEBUG) + printk ("[fan:] fault:%d, r_fault=%d, direction=%d, ctrl_speed=%d \n",fault, r_fault, direction, ctrl_speed); + + for (i=0; istatus[i] = (fault & fan_info_mask[i]) >> i; + if (LOCAL_DEBUG) + printk ("[fan%d:] fail=%d \n",i, fan_data->status[i]); + + fan_data->r_status[i] = (r_fault & fan_info_mask[i]) >> i; + fan_data->direction[i] = (direction & fan_info_mask[i]) >> i; + fan_data->duty_cycle[i] = ctrl_speed * FAN_SPEED_PRECENT_TO_CPLD_STEP; + + /* fan speed + */ + speed = accton_as5712_54x_fan_read_value(fan_speed_reg[i]); + r_speed = accton_as5712_54x_fan_read_value(fanr_speed_reg[i]); + if ( (speed < 0) || (r_speed < 0) ) + { + if (LOCAL_DEBUG) + printk ("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); + goto _exit; /* error */ + } + + if (LOCAL_DEBUG) + printk ("[fan%d:] speed:%d, r_speed=%d \n", i, speed, r_speed); + + fan_data->speed[i] = speed * FAN_SPEED_CPLD_TO_RPM_STEP; + fan_data->r_speed[i] = r_speed * FAN_SPEED_CPLD_TO_RPM_STEP; + } + + /* finish to update */ + fan_data->last_updated = jiffies; + fan_data->valid = 1; + +_exit: + mutex_unlock(&fan_data->update_lock); +} + +static int accton_as5712_54x_fan_probe(struct platform_device *pdev) +{ + int status = -1; + + /* Register sysfs hooks */ + status = sysfs_create_group(&pdev->dev.kobj, &accton_as5712_54x_fan_group); + if (status) { + goto exit; + + } + + fan_data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(fan_data->hwmon_dev)) { + status = PTR_ERR(fan_data->hwmon_dev); + goto exit_remove; + } + + dev_info(&pdev->dev, "accton_as5712_54x_fan\n"); + + return 0; + +exit_remove: + sysfs_remove_group(&pdev->dev.kobj, &accton_as5712_54x_fan_group); +exit: + return status; +} + +static int accton_as5712_54x_fan_remove(struct platform_device *pdev) +{ + hwmon_device_unregister(fan_data->hwmon_dev); + sysfs_remove_group(&fan_data->pdev->dev.kobj, &accton_as5712_54x_fan_group); + + return 0; +} + +#define DRVNAME "as5712_54x_fan" + +static struct platform_driver accton_as5712_54x_fan_driver = { + .probe = accton_as5712_54x_fan_probe, + .remove = accton_as5712_54x_fan_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init accton_as5712_54x_fan_init(void) +{ + int ret; + + ret = platform_driver_register(&accton_as5712_54x_fan_driver); + if (ret < 0) { + goto exit; + } + + fan_data = kzalloc(sizeof(struct accton_as5712_54x_fan), GFP_KERNEL); + if (!fan_data) { + ret = -ENOMEM; + platform_driver_unregister(&accton_as5712_54x_fan_driver); + goto exit; + } + + mutex_init(&fan_data->update_lock); + fan_data->valid = 0; + + fan_data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(fan_data->pdev)) { + ret = PTR_ERR(fan_data->pdev); + platform_driver_unregister(&accton_as5712_54x_fan_driver); + kfree(fan_data); + goto exit; + } + +exit: + return ret; +} + +static void __exit accton_as5712_54x_fan_exit(void) +{ + platform_device_unregister(fan_data->pdev); + platform_driver_unregister(&accton_as5712_54x_fan_driver); + kfree(fan_data); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_as5712_54x_fan driver"); +MODULE_LICENSE("GPL"); + +module_init(accton_as5712_54x_fan_init); +module_exit(accton_as5712_54x_fan_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/accton_as5712_54x_psu.c b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/accton_as5712_54x_psu.c new file mode 100755 index 000000000000..9a0d1dae58ba --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/accton_as5712_54x_psu.c @@ -0,0 +1,371 @@ +/* + * An hwmon driver for accton as5712_54x Power Module + * + * Copyright (C) 2015 Accton Technology Corporation. + * Copyright (C) Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * 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. + */ +#if 0 +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PSU_STATUS_I2C_ADDR 0x60 +#define PSU_STATUS_I2C_REG_OFFSET 0x2 + +#define IS_POWER_GOOD(id, value) (!!(value & BIT(id*4 + 1))) +#define IS_PRESENT(id, value) (!(value & BIT(id*4))) + +static ssize_t show_index(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, char *buf); +static int as5712_54x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); +extern int as5712_54x_cpld_read(unsigned short cpld_addr, u8 reg); +static int as5712_54x_psu_model_name_get(struct device *dev); + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as5712_54x_psu_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + u8 status; /* Status(present/power_good) register read from CPLD */ + char model_name[14]; /* Model name, read from eeprom */ +}; + +static struct as5712_54x_psu_data *as5712_54x_psu_update_device(struct device *dev); + +enum as5712_54x_psu_sysfs_attributes { + PSU_INDEX, + PSU_PRESENT, + PSU_MODEL_NAME, + PSU_POWER_GOOD +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_index, S_IRUGO, show_index, NULL, PSU_INDEX); +static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_status, NULL, PSU_PRESENT); +static SENSOR_DEVICE_ATTR(psu_model_name, S_IRUGO, show_model_name,NULL, PSU_MODEL_NAME); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD); + +static struct attribute *as5712_54x_psu_attributes[] = { + &sensor_dev_attr_psu_index.dev_attr.attr, + &sensor_dev_attr_psu_present.dev_attr.attr, + &sensor_dev_attr_psu_model_name.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + NULL +}; + +static ssize_t show_index(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_psu_data *data = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", data->index); +} + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as5712_54x_psu_data *data = as5712_54x_psu_update_device(dev); + u8 status = 0; + + if (!data->valid) { + return sprintf(buf, "0\n"); + } + + if (attr->index == PSU_PRESENT) { + status = IS_PRESENT(data->index, data->status); + } + else { /* PSU_POWER_GOOD */ + status = IS_POWER_GOOD(data->index, data->status); + } + + return sprintf(buf, "%d\n", status); +} + +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as5712_54x_psu_data *data = as5712_54x_psu_update_device(dev); + + if (!data->valid) { + return 0; + } + + if (!IS_PRESENT(data->index, data->status)) { + return 0; + } + + if (as5712_54x_psu_model_name_get(dev) < 0) { + return -ENXIO; + } + + return sprintf(buf, "%s\n", data->model_name); +} + +static const struct attribute_group as5712_54x_psu_group = { + .attrs = as5712_54x_psu_attributes, +}; + +static int as5712_54x_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as5712_54x_psu_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as5712_54x_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as5712_54x_psu_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as5712_54x_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as5712_54x_psu_remove(struct i2c_client *client) +{ + struct as5712_54x_psu_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as5712_54x_psu_group); + kfree(data); + + return 0; +} + +enum psu_index +{ + as5712_54x_psu1, + as5712_54x_psu2 +}; + +static const struct i2c_device_id as5712_54x_psu_id[] = { + { "as5712_54x_psu1", as5712_54x_psu1 }, + { "as5712_54x_psu2", as5712_54x_psu2 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as5712_54x_psu_id); + +static struct i2c_driver as5712_54x_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as5712_54x_psu", + }, + .probe = as5712_54x_psu_probe, + .remove = as5712_54x_psu_remove, + .id_table = as5712_54x_psu_id, + .address_list = normal_i2c, +}; + +static int as5712_54x_psu_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; +} + +enum psu_type { + PSU_YM_2401_JCR, /* AC110V - F2B */ + PSU_YM_2401_JDR, /* AC110V - B2F */ + PSU_CPR_4011_4M11, /* AC110V - F2B */ + PSU_CPR_4011_4M21, /* AC110V - B2F */ + PSU_CPR_6011_2M11, /* AC110V - F2B */ + PSU_CPR_6011_2M21, /* AC110V - B2F */ + PSU_UM400D_01G, /* DC48V - F2B */ + PSU_UM400D01_01G /* DC48V - B2F */ +}; + +struct model_name_info { + enum psu_type type; + u8 offset; + u8 length; + char* model_name; +}; + +struct model_name_info models[] = { +{PSU_YM_2401_JCR, 0x20, 11, "YM-2401JCR"}, +{PSU_YM_2401_JDR, 0x20, 11, "YM-2401JDR"}, +{PSU_CPR_4011_4M11, 0x26, 13, "CPR-4011-4M11"}, +{PSU_CPR_4011_4M21, 0x26, 13, "CPR-4011-4M21"}, +{PSU_CPR_6011_2M11, 0x26, 13, "CPR-6011-2M11"}, +{PSU_CPR_6011_2M21, 0x26, 13, "CPR-6011-2M21"}, +{PSU_UM400D_01G, 0x50, 9, "um400d01G"}, +{PSU_UM400D01_01G, 0x50, 12, "um400d01-01G"}, +}; + +static int as5712_54x_psu_model_name_get(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_psu_data *data = i2c_get_clientdata(client); + int i, status; + + for (i = 0; i < ARRAY_SIZE(models); i++) { + memset(data->model_name, 0, sizeof(data->model_name)); + + status = as5712_54x_psu_read_block(client, models[i].offset, + data->model_name, models[i].length); + if (status < 0) { + data->model_name[0] = '\0'; + dev_dbg(&client->dev, "unable to read model name from (0x%x) offset(0x%x)\n", + client->addr, models[i].offset); + return status; + } + else { + data->model_name[models[i].length] = '\0'; + } + + if (i == PSU_YM_2401_JCR || i == PSU_YM_2401_JDR) { + /* Skip the meaningless data byte 8*/ + data->model_name[8] = data->model_name[9]; + data->model_name[9] = data->model_name[10]; + data->model_name[10] = '\0'; + } + + /* Determine if the model name is known, if not, read next index + */ + if (strncmp(data->model_name, models[i].model_name, models[i].length) == 0) { + return 0; + } + else { + data->model_name[0] = '\0'; + } + } + + return -ENODATA; +} + +static struct as5712_54x_psu_data *as5712_54x_psu_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_psu_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int status = -1; + + dev_dbg(&client->dev, "Starting as5712_54x update\n"); + data->valid = 0; + + + /* Read psu status */ + status = as5712_54x_cpld_read(PSU_STATUS_I2C_ADDR, PSU_STATUS_I2C_REG_OFFSET); + + if (status < 0) { + dev_dbg(&client->dev, "cpld reg (0x%x) err %d\n", PSU_STATUS_I2C_ADDR, status); + goto exit; + } + else { + data->status = status; + } + + data->last_updated = jiffies; + data->valid = 1; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init as5712_54x_psu_init(void) +{ + return i2c_add_driver(&as5712_54x_psu_driver); +} + +static void __exit as5712_54x_psu_exit(void) +{ + i2c_del_driver(&as5712_54x_psu_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton as5712_54x_psu driver"); +MODULE_LICENSE("GPL"); + +module_init(as5712_54x_psu_init); +module_exit(as5712_54x_psu_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/accton_as5712_54x_sfp.c b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/accton_as5712_54x_sfp.c new file mode 100755 index 000000000000..d236057c4e59 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/accton_as5712_54x_sfp.c @@ -0,0 +1,825 @@ +/* + * An hwmon driver for accton as5712_54x sfp + * + * Copyright (C) Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * 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. + */ +#if 0 +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUM_OF_SFF_PORT 54 +#define SFP_PORT_MAX 48 +#define I2C_ADDR_CPLD1 0x60 +#define I2C_ADDR_CPLD2 0x61 +#define I2C_ADDR_CPLD3 0x62 +#define CPLD3_OFFSET_QSFP_MOD_RST 0x15 +#define CPLD3_OFFSET_QSFP_LPMODE 0x16 + + +#define BIT_INDEX(i) (1ULL << (i)) + +#if 0 +static ssize_t show_status(struct device *dev, struct device_attribute *da,char *buf); +static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, char *buf); +static int as5712_54x_sfp_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); +extern int as5712_54x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int as5712_54x_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); +#endif + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as5712_54x_sfp_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + int port; /* Front port index */ + char eeprom[256]; /* eeprom data */ + u64 status[4]; /* bit0:port0, bit1:port1 and so on */ + /* index 0 => is_present + 1 => tx_fail + 2 => tx_disable + 3 => rx_loss */ +}; + +/* The table maps active port to cpld port. + * Array index 0 is for active port 1, + * index 1 for active port 2, and so on. + * The array content implies cpld port index. + */ +static const u8 cpld_to_front_port_table[] = +{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 52, 50, 53, 51, 54}; + +#define CPLD_PORT_TO_FRONT_PORT(port) (cpld_to_front_port_table[port]) + +static struct as5712_54x_sfp_data *as5712_54x_sfp_update_device(struct device *dev, int update_eeprom); +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t get_lp_mode(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t set_lp_mode(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t get_mode_reset(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t set_mode_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +extern int as5712_54x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int as5712_54x_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +enum as5712_54x_sfp_sysfs_attributes { + SFP_IS_PRESENT, + SFP_TX_FAULT, + SFP_TX_DISABLE, + SFP_RX_LOSS, + SFP_PORT_NUMBER, + SFP_EEPROM, + SFP_RX_LOS_ALL, + SFP_IS_PRESENT_ALL, + SFP_LP_MODE, + SFP_MOD_RST, +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_status, NULL, SFP_IS_PRESENT); +static SENSOR_DEVICE_ATTR(sfp_tx_fault, S_IRUGO, show_status, NULL, SFP_TX_FAULT); +static SENSOR_DEVICE_ATTR(sfp_tx_disable, S_IWUSR | S_IRUGO, show_status, set_tx_disable, SFP_TX_DISABLE); +static SENSOR_DEVICE_ATTR(sfp_rx_loss, S_IRUGO, show_status,NULL, SFP_RX_LOSS); +static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, SFP_PORT_NUMBER); +static SENSOR_DEVICE_ATTR(sfp_eeprom, S_IRUGO, show_eeprom, NULL, SFP_EEPROM); +static SENSOR_DEVICE_ATTR(sfp_rx_los_all, S_IRUGO, show_status,NULL, SFP_RX_LOS_ALL); +static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_status,NULL, SFP_IS_PRESENT_ALL); +static SENSOR_DEVICE_ATTR(sfp_lp_mode, S_IWUSR | S_IRUGO, get_lp_mode, set_lp_mode, SFP_LP_MODE); +static SENSOR_DEVICE_ATTR(sfp_mod_rst, S_IWUSR | S_IRUGO, get_mode_reset, set_mode_reset, SFP_MOD_RST); + +static struct attribute *as5712_54x_sfp_attributes[] = { + &sensor_dev_attr_sfp_is_present.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault.dev_attr.attr, + &sensor_dev_attr_sfp_rx_loss.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable.dev_attr.attr, + &sensor_dev_attr_sfp_eeprom.dev_attr.attr, + &sensor_dev_attr_sfp_port_number.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los_all.dev_attr.attr, + &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, + &sensor_dev_attr_sfp_lp_mode.dev_attr.attr, + &sensor_dev_attr_sfp_mod_rst.dev_attr.attr, + NULL +}; + +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", CPLD_PORT_TO_FRONT_PORT(data->port)); +} + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as5712_54x_sfp_data *data; + u8 val; + int values[7]; + + /* Error-check the CPLD read results. */ +#define VALIDATED_READ(_buf, _rv, _read_expr, _invert) \ + do { \ + _rv = (_read_expr); \ + if(_rv < 0) { \ + return sprintf(_buf, "READ ERROR\n"); \ + } \ + if(_invert) { \ + _rv = ~_rv; \ + } \ + _rv &= 0xFF; \ + } while(0) + + if(attr->index == SFP_RX_LOS_ALL) { + /* + * Report the RX_LOS status for all ports. + * This does not depend on the currently active SFP selector. + */ + + /* RX_LOS Ports 1-8 */ + VALIDATED_READ(buf, values[0], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD2, 0x0F), 0); + /* RX_LOS Ports 9-16 */ + VALIDATED_READ(buf, values[1], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD2, 0x10), 0); + /* RX_LOS Ports 17-24 */ + VALIDATED_READ(buf, values[2], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD2, 0x11), 0); + /* RX_LOS Ports 25-32 */ + VALIDATED_READ(buf, values[3], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, 0x0F), 0); + /* RX_LOS Ports 33-40 */ + VALIDATED_READ(buf, values[4], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, 0x10), 0); + /* RX_LOS Ports 41-48 */ + VALIDATED_READ(buf, values[5], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, 0x11), 0); + + /** Return values 1 -> 48 in order */ + return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], + values[3], values[4], values[5]); + } + + if(attr->index == SFP_IS_PRESENT_ALL) { + /* + * Report the SFP_PRESENCE status for all ports. + * This does not depend on the currently active SFP selector. + */ + + /* SFP_PRESENT Ports 1-8 */ + VALIDATED_READ(buf, values[0], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD2, 0x6), 1); + /* SFP_PRESENT Ports 9-16 */ + VALIDATED_READ(buf, values[1], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD2, 0x7), 1); + /* SFP_PRESENT Ports 17-24 */ + VALIDATED_READ(buf, values[2], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD2, 0x8), 1); + /* SFP_PRESENT Ports 25-32 */ + VALIDATED_READ(buf, values[3], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, 0x6), 1); + /* SFP_PRESENT Ports 33-40 */ + VALIDATED_READ(buf, values[4], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, 0x7), 1); + /* SFP_PRESENT Ports 41-48 */ + VALIDATED_READ(buf, values[5], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, 0x8), 1); + /* QSFP_PRESENT Ports 49-54 */ + VALIDATED_READ(buf, values[6], as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, 0x14), 1); + + /* Return values 1 -> 54 in order */ + return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], + values[3], values[4], values[5], + values[6] & 0x3F); + } + /* + * The remaining attributes are gathered on a per-selected-sfp basis. + */ + data = as5712_54x_sfp_update_device(dev, 0); + if (attr->index == SFP_IS_PRESENT) { + val = (data->status[attr->index] & BIT_INDEX(data->port)) ? 0 : 1; + } + else { + val = (data->status[attr->index] & BIT_INDEX(data->port)) ? 1 : 0; + } + + return sprintf(buf, "%d", val); +} + +static ssize_t get_lp_mode(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); + u8 cpld_val = 0; + int port_bit; + int status = -EINVAL; + + /* Low power mode is not supported for SFP ports(1-48) */ + if (data->port < SFP_PORT_MAX) { + return -EINVAL; + } + mutex_lock(&data->update_lock); + + port_bit = data->port - SFP_PORT_MAX; + cpld_val = as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, CPLD3_OFFSET_QSFP_LPMODE); + cpld_val = cpld_val & 0x3F; + cpld_val = cpld_val & BIT_INDEX(port_bit); + status = snprintf(buf, PAGE_SIZE - 1, "%d\r\n", cpld_val>>port_bit); + + mutex_unlock(&data->update_lock); + + return status; +} + +static ssize_t set_lp_mode(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); + u8 cpld_val = 0; + long mode; + int error, port_bit; + + /* Tx disable is not supported for QSFP ports(49-54) */ + if (data->port < SFP_PORT_MAX) { + return -EINVAL; + } + port_bit = data->port - SFP_PORT_MAX; + error = kstrtol(buf, 10, &mode); + if (error) { + return error; + } + mutex_lock(&data->update_lock); + + cpld_val = as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, CPLD3_OFFSET_QSFP_LPMODE); + /* Update lp_mode status */ + if (mode) + { + cpld_val |= BIT_INDEX(port_bit); + } + else + { + cpld_val &=~BIT_INDEX(port_bit); + } + as5712_54x_i2c_cpld_write(I2C_ADDR_CPLD3, CPLD3_OFFSET_QSFP_LPMODE, cpld_val); + + mutex_unlock(&data->update_lock); + + return count; +} + + +static ssize_t get_mode_reset(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); + u8 cpld_val = 0; + int port_bit; + int status = -EINVAL; + + /* Low power mode is not supported for SFP ports(1-48) */ + if (data->port < SFP_PORT_MAX) { + return -EINVAL; + } + mutex_lock(&data->update_lock); + + port_bit = data->port - SFP_PORT_MAX; + cpld_val = as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, CPLD3_OFFSET_QSFP_MOD_RST); + cpld_val = cpld_val & 0x3F; + cpld_val = cpld_val & BIT_INDEX(port_bit); + status = snprintf(buf, PAGE_SIZE - 1, "%d\r\n", cpld_val>>port_bit); + + mutex_unlock(&data->update_lock); + + return status; +} + +static ssize_t set_mode_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); + u8 cpld_val = 0; + long reset; + int error, port_bit; + + /* Tx disable is not supported for QSFP ports(49-54) */ + if (data->port < SFP_PORT_MAX) { + return -EINVAL; + } + port_bit = data->port - SFP_PORT_MAX; + error = kstrtol(buf, 10, &reset); + if (error) { + return error; + } + mutex_lock(&data->update_lock); + + cpld_val = as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, CPLD3_OFFSET_QSFP_MOD_RST); + /* Update lp_mode status */ + if (reset) + { + cpld_val |= BIT_INDEX(port_bit); + } + else + { + cpld_val &=~BIT_INDEX(port_bit); + } + as5712_54x_i2c_cpld_write(I2C_ADDR_CPLD3, CPLD3_OFFSET_QSFP_MOD_RST, cpld_val); + + mutex_unlock(&data->update_lock); + + return count; +} + + +static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); + unsigned short cpld_addr = 0; + u8 cpld_reg = 0, cpld_val = 0, cpld_bit = 0; + long disable; + int error; + + /* Tx disable is not supported for QSFP ports(49-54) */ + if (data->port >= SFP_PORT_MAX) { + return -EINVAL; + } + + error = kstrtol(buf, 10, &disable); + if (error) { + return error; + } + + mutex_lock(&data->update_lock); + + if(data->port < 24) { + cpld_addr = I2C_ADDR_CPLD2; + cpld_reg = 0xC + data->port / 8; + cpld_bit = 1 << (data->port % 8); + } + else { + cpld_addr = I2C_ADDR_CPLD3; + cpld_reg = 0xC + (data->port - 24) / 8; + cpld_bit = 1 << (data->port % 8); + } + + cpld_val = as5712_54x_i2c_cpld_read(cpld_addr, cpld_reg); + + /* Update tx_disable status */ + if (disable) { + data->status[SFP_TX_DISABLE] |= BIT_INDEX(data->port); + cpld_val |= cpld_bit; + } + else { + data->status[SFP_TX_DISABLE] &= ~BIT_INDEX(data->port); + cpld_val &= ~cpld_bit; + } + + as5712_54x_i2c_cpld_write(cpld_addr, cpld_reg, cpld_val); + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as5712_54x_sfp_data *data = as5712_54x_sfp_update_device(dev, 1); + + if (!data->valid) { + return 0; + } + + if ((data->status[SFP_IS_PRESENT] & BIT_INDEX(data->port)) != 0) { + return 0; + } + + memcpy(buf, data->eeprom, sizeof(data->eeprom)); + + return sizeof(data->eeprom); +} + +static const struct attribute_group as5712_54x_sfp_group = { + .attrs = as5712_54x_sfp_attributes, +}; + +static int as5712_54x_sfp_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as5712_54x_sfp_data *data; + int status; + + extern int platform_accton_as5712_54x(void); + if(!platform_accton_as5712_54x()) { + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as5712_54x_sfp_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + mutex_init(&data->update_lock); + data->port = dev_id->driver_data; + i2c_set_clientdata(client, data); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as5712_54x_sfp_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: sfp '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as5712_54x_sfp_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as5712_54x_sfp_remove(struct i2c_client *client) +{ + struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as5712_54x_sfp_group); + kfree(data); + + return 0; +} + +enum port_numbers { +as5712_54x_sfp1, as5712_54x_sfp2, as5712_54x_sfp3, as5712_54x_sfp4, +as5712_54x_sfp5, as5712_54x_sfp6, as5712_54x_sfp7, as5712_54x_sfp8, +as5712_54x_sfp9, as5712_54x_sfp10, as5712_54x_sfp11,as5712_54x_sfp12, +as5712_54x_sfp13, as5712_54x_sfp14, as5712_54x_sfp15,as5712_54x_sfp16, +as5712_54x_sfp17, as5712_54x_sfp18, as5712_54x_sfp19,as5712_54x_sfp20, +as5712_54x_sfp21, as5712_54x_sfp22, as5712_54x_sfp23,as5712_54x_sfp24, +as5712_54x_sfp25, as5712_54x_sfp26, as5712_54x_sfp27,as5712_54x_sfp28, +as5712_54x_sfp29, as5712_54x_sfp30, as5712_54x_sfp31,as5712_54x_sfp32, +as5712_54x_sfp33, as5712_54x_sfp34, as5712_54x_sfp35,as5712_54x_sfp36, +as5712_54x_sfp37, as5712_54x_sfp38, as5712_54x_sfp39,as5712_54x_sfp40, +as5712_54x_sfp41, as5712_54x_sfp42, as5712_54x_sfp43,as5712_54x_sfp44, +as5712_54x_sfp45, as5712_54x_sfp46, as5712_54x_sfp47,as5712_54x_sfp48, +as5712_54x_sfp49, as5712_54x_sfp52, as5712_54x_sfp50,as5712_54x_sfp53, +as5712_54x_sfp51, as5712_54x_sfp54 +}; + +static const struct i2c_device_id as5712_54x_sfp_id[] = { +{ "as5712_54x_sfp1", as5712_54x_sfp1 }, { "as5712_54x_sfp2", as5712_54x_sfp2 }, +{ "as5712_54x_sfp3", as5712_54x_sfp3 }, { "as5712_54x_sfp4", as5712_54x_sfp4 }, +{ "as5712_54x_sfp5", as5712_54x_sfp5 }, { "as5712_54x_sfp6", as5712_54x_sfp6 }, +{ "as5712_54x_sfp7", as5712_54x_sfp7 }, { "as5712_54x_sfp8", as5712_54x_sfp8 }, +{ "as5712_54x_sfp9", as5712_54x_sfp9 }, { "as5712_54x_sfp10", as5712_54x_sfp10 }, +{ "as5712_54x_sfp11", as5712_54x_sfp11 }, { "as5712_54x_sfp12", as5712_54x_sfp12 }, +{ "as5712_54x_sfp13", as5712_54x_sfp13 }, { "as5712_54x_sfp14", as5712_54x_sfp14 }, +{ "as5712_54x_sfp15", as5712_54x_sfp15 }, { "as5712_54x_sfp16", as5712_54x_sfp16 }, +{ "as5712_54x_sfp17", as5712_54x_sfp17 }, { "as5712_54x_sfp18", as5712_54x_sfp18 }, +{ "as5712_54x_sfp19", as5712_54x_sfp19 }, { "as5712_54x_sfp20", as5712_54x_sfp20 }, +{ "as5712_54x_sfp21", as5712_54x_sfp21 }, { "as5712_54x_sfp22", as5712_54x_sfp22 }, +{ "as5712_54x_sfp23", as5712_54x_sfp23 }, { "as5712_54x_sfp24", as5712_54x_sfp24 }, +{ "as5712_54x_sfp25", as5712_54x_sfp25 }, { "as5712_54x_sfp26", as5712_54x_sfp26 }, +{ "as5712_54x_sfp27", as5712_54x_sfp27 }, { "as5712_54x_sfp28", as5712_54x_sfp28 }, +{ "as5712_54x_sfp29", as5712_54x_sfp29 }, { "as5712_54x_sfp30", as5712_54x_sfp30 }, +{ "as5712_54x_sfp31", as5712_54x_sfp31 }, { "as5712_54x_sfp32", as5712_54x_sfp32 }, +{ "as5712_54x_sfp33", as5712_54x_sfp33 }, { "as5712_54x_sfp34", as5712_54x_sfp34 }, +{ "as5712_54x_sfp35", as5712_54x_sfp35 }, { "as5712_54x_sfp36", as5712_54x_sfp36 }, +{ "as5712_54x_sfp37", as5712_54x_sfp37 }, { "as5712_54x_sfp38", as5712_54x_sfp38 }, +{ "as5712_54x_sfp39", as5712_54x_sfp39 }, { "as5712_54x_sfp40", as5712_54x_sfp40 }, +{ "as5712_54x_sfp41", as5712_54x_sfp41 }, { "as5712_54x_sfp42", as5712_54x_sfp42 }, +{ "as5712_54x_sfp43", as5712_54x_sfp43 }, { "as5712_54x_sfp44", as5712_54x_sfp44 }, +{ "as5712_54x_sfp45", as5712_54x_sfp45 }, { "as5712_54x_sfp46", as5712_54x_sfp46 }, +{ "as5712_54x_sfp47", as5712_54x_sfp47 }, { "as5712_54x_sfp48", as5712_54x_sfp48 }, +{ "as5712_54x_sfp49", as5712_54x_sfp49 }, { "as5712_54x_sfp50", as5712_54x_sfp50 }, +{ "as5712_54x_sfp51", as5712_54x_sfp51 }, { "as5712_54x_sfp52", as5712_54x_sfp52 }, +{ "as5712_54x_sfp53", as5712_54x_sfp53 }, { "as5712_54x_sfp54", as5712_54x_sfp54 }, + +{} +}; +MODULE_DEVICE_TABLE(i2c, as5712_54x_sfp_id); + +static struct i2c_driver as5712_54x_sfp_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as5712_54x_sfp", + }, + .probe = as5712_54x_sfp_probe, + .remove = as5712_54x_sfp_remove, + .id_table = as5712_54x_sfp_id, + .address_list = normal_i2c, +}; + +static int as5712_54x_sfp_read_byte(struct i2c_client *client, u8 command, u8 *data) +{ + int result = i2c_smbus_read_byte_data(client, command); + + if (unlikely(result < 0)) { + dev_dbg(&client->dev, "sfp read byte data failed, command(0x%2x), data(0x%2x)\r\n", command, result); + goto abort; + } + + *data = (u8)result; + result = 0; + +abort: + return result; +} + +#define ALWAYS_UPDATE_DEVICE 1 + +static struct as5712_54x_sfp_data *as5712_54x_sfp_update_device(struct device *dev, int update_eeprom) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (ALWAYS_UPDATE_DEVICE || time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int status = -1; + int i = 0, j = 0; + + data->valid = 0; + //dev_dbg(&client->dev, "Starting as5712_54x sfp status update\n"); + memset(data->status, 0, sizeof(data->status)); + + /* Read status of port 1~48(SFP port) */ + for (i = 0; i < 2; i++) { + for (j = 0; j < 12; j++) { + status = as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD2+i, 0x6+j); + + if (status < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", I2C_ADDR_CPLD2+i, 0x6+j, status); + goto exit; + } + + data->status[j/3] |= (u64)status << ((i*24) + (j%3)*8); + } + } + + /* + * Bring QSFPs out of reset, + * This is a temporary fix until the QSFP+_MOD_RST register + * can be exposed through the driver. + */ + as5712_54x_i2c_cpld_write(I2C_ADDR_CPLD3, 0x15, 0x3F); + + /* Read present status of port 49-54(QSFP port) */ + status = as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, 0x14); + + if (status < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", I2C_ADDR_CPLD2+i, 0x6+j, status); + } + else { + data->status[SFP_IS_PRESENT] |= (u64)status << 48; + } + + if (update_eeprom) { + /* Read eeprom data based on port number */ + memset(data->eeprom, 0, sizeof(data->eeprom)); + + /* Check if the port is present */ + if ((data->status[SFP_IS_PRESENT] & BIT_INDEX(data->port)) == 0) { + /* read eeprom */ + for (i = 0; i < sizeof(data->eeprom); i++) { + status = as5712_54x_sfp_read_byte(client, i, data->eeprom + i); + + if (status < 0) { + dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", + CPLD_PORT_TO_FRONT_PORT(data->port)); + goto exit; + } + } + } + } + + data->valid = 1; + data->last_updated = jiffies; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +module_i2c_driver(as5712_54x_sfp_driver); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton as5712_54x_sfp driver"); +MODULE_LICENSE("GPL"); + +#if 0 + int i = 0, j = 0; + + data->valid = 0; + //dev_dbg(&client->dev, "Starting as5712_54x sfp update\n"); + memset(data->status, 0, sizeof(data->status)); + + /* Read status of port 1~48(SFP port) */ + for (i = 0; i < 2; i++) { + for (j = 0; j < 12; j++) { + status = as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD2+i, 0x6+j); + + if (status < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", I2C_ADDR_CPLD2+i, 0x6+j, status); + continue; + } + + data->status[j/3] |= (u64)status << ((i*24) + (j%3)*8); + } + } + + /* Read present status of port 49-54(QSFP port) */ + status = as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, 0x14); + + if (status < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", I2C_ADDR_CPLD2+i, 0x6+j, status); + } + else { + data->status[SFP_IS_PRESENT] |= (u64)status << 48; + } +#endif + +/* Reserver to prevent from CPLD port mapping is changed + */ +#if 0 +BIT_INDEX(port_present_index[data->port]) +/* The bit index of is_present field read from CPLD + * Array index 0 is for as5712_54x_sfp1, + * index 1 is for as5712_54x_sfp2, and so on. + */ +static const int port_present_index[] = { + 4, 5, 6, 7, 9, 8, 11, 10, + 0, 1, 2, 3, 12, 13, 14, 15, +16, 17, 18, 19, 28, 29, 30, 31, +20, 21, 22, 23, 24, 25, 26, 27 +}; +#endif + +#if 0 +static struct as5712_54x_sfp_data *as5712_54x_sfp_update_status(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); + int status = -1; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->status_last_updated + HZ + HZ / 2) + || !data->status_valid) { + int status = -1; + int i = 0, j = 0; + + data->status_valid = 0; + //dev_dbg(&client->dev, "Starting as5712_54x sfp status update\n"); + memset(data->status, 0, sizeof(data->status)); + + /* Read status of port 1~48(SFP port) */ + for (i = 0; i < 2; i++) { + for (j = 0; j < 12; j++) { + status = as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD2+i, 0x6+j); + + if (status < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", I2C_ADDR_CPLD2+i, 0x6+j, status); + goto exit; + } + + data->status[j/3] |= (u64)status << ((i*24) + (j%3)*8); + } + } + + /* + * Bring QSFPs out of reset, + * This is a temporary fix until the QSFP+_MOD_RST register + * can be exposed through the driver. + */ + as5712_54x_i2c_cpld_write(I2C_ADDR_CPLD3, 0x15, 0x3F); + + /* Read present status of port 49-54(QSFP port) */ + status = as5712_54x_i2c_cpld_read(I2C_ADDR_CPLD3, 0x14); + + if (status < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", I2C_ADDR_CPLD2+i, 0x6+j, status); + } + else { + data->status[SFP_IS_PRESENT] |= (u64)status << 48; + } + + data->status_valid = 1; + data->status_last_updated = jiffies; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +static struct as5712_54x_sfp_data *as5712_54x_sfp_update_eeprom(struct device *dev) +{ + struct as5712_54x_sfp_data *data = NULL; + + data = as5712_54x_sfp_update_status(dev); + + if (data == NULL || data->status_valid == 0) { + data->eeprom_valid = 0; + return data; + } + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->eeprom_last_updated + HZ + HZ / 2) + || !data->eeprom_valid) { + int status = -1; + int i = 0; + + /* Read eeprom data based on port number */ + memset(data->eeprom, 0, sizeof(data->eeprom)); + + /* Check if the port is present */ + if ((data->status[SFP_IS_PRESENT] & BIT_INDEX(data->port)) == 0) { + /* read eeprom */ + for (i = 0; i < sizeof(data->eeprom)/I2C_SMBUS_BLOCK_MAX; i++) { + status = as5712_54x_sfp_read_block(client, i*I2C_SMBUS_BLOCK_MAX, + data->eeprom+(i*I2C_SMBUS_BLOCK_MAX), + I2C_SMBUS_BLOCK_MAX); + if (status < 0) { + dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", + CPLD_PORT_TO_FRONT_PORT(data->port)); + goto exit; + } + } + } + + data->eeprom_last_updated = jiffies; + data->eeprom_valid = 1; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} +#endif diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/cpr_4011_4mxx.c b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/cpr_4011_4mxx.c new file mode 100755 index 000000000000..30bea914d589 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/cpr_4011_4mxx.c @@ -0,0 +1,400 @@ +/* + * An hwmon driver for the CPR-4011-4Mxx Redundant Power Module + * + * Copyright (C) Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * 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. + */ +#if 0 +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_FAN_DUTY_CYCLE 100 + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x3c, 0x3d, 0x3e, 0x3f, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct cpr_4011_4mxx_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 vout_mode; /* Register value */ + u16 v_in; /* Register value */ + u16 v_out; /* Register value */ + u16 i_in; /* Register value */ + u16 i_out; /* Register value */ + u16 p_in; /* Register value */ + u16 p_out; /* Register value */ + u16 temp_input[2]; /* Register value */ + u8 fan_fault; /* Register value */ + u16 fan_duty_cycle[2]; /* Register value */ + u16 fan_speed[2]; /* Register value */ +}; + +static ssize_t show_linear(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_vout(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da, const char *buf, size_t count); +static int cpr_4011_4mxx_write_word(struct i2c_client *client, u8 reg, u16 value); +static struct cpr_4011_4mxx_data *cpr_4011_4mxx_update_device(struct device *dev); + +enum cpr_4011_4mxx_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, +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_v_in, S_IRUGO, show_linear, NULL, PSU_V_IN); +static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, show_vout, NULL, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(psu_i_in, S_IRUGO, show_linear, NULL, PSU_I_IN); +static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, show_linear, NULL, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(psu_p_in, S_IRUGO, show_linear, NULL, PSU_P_IN); +static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, show_linear, NULL, PSU_P_OUT); +static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_fan_fault, NULL, PSU_FAN1_FAULT); +static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IWUSR | S_IRUGO, show_linear, set_fan_duty_cycle, PSU_FAN1_DUTY_CYCLE); +static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); + +static struct attribute *cpr_4011_4mxx_attributes[] = { + &sensor_dev_attr_psu_v_in.dev_attr.attr, + &sensor_dev_attr_psu_v_out.dev_attr.attr, + &sensor_dev_attr_psu_i_in.dev_attr.attr, + &sensor_dev_attr_psu_i_out.dev_attr.attr, + &sensor_dev_attr_psu_p_in.dev_attr.attr, + &sensor_dev_attr_psu_p_out.dev_attr.attr, + &sensor_dev_attr_psu_temp1_input.dev_attr.attr, + &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, + &sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr, + &sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr, + NULL +}; + +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(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpr_4011_4mxx_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; + + mutex_lock(&data->update_lock); + data->fan_duty_cycle[nr] = speed; + cpr_4011_4mxx_write_word(client, 0x3B + nr, data->fan_duty_cycle[nr]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_linear(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpr_4011_4mxx_data *data = cpr_4011_4mxx_update_device(dev); + + u16 value = 0; + int exponent, mantissa; + int multiplier = 1000; + + switch (attr->index) { + case PSU_V_IN: + value = data->v_in; + break; + case PSU_I_IN: + value = data->i_in; + break; + case PSU_I_OUT: + value = data->i_out; + break; + case PSU_P_IN: + value = data->p_in; + break; + case PSU_P_OUT: + value = data->p_out; + break; + case PSU_TEMP1_INPUT: + value = data->temp_input[0]; + break; + case PSU_FAN1_DUTY_CYCLE: + multiplier = 1; + value = data->fan_duty_cycle[0]; + break; + case PSU_FAN1_SPEED: + multiplier = 1; + value = data->fan_speed[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 show_fan_fault(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpr_4011_4mxx_data *data = cpr_4011_4mxx_update_device(dev); + + u8 shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6; + + return sprintf(buf, "%d\n", data->fan_fault >> shift); +} + +static ssize_t show_vout(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct cpr_4011_4mxx_data *data = cpr_4011_4mxx_update_device(dev); + int exponent, mantissa; + int multiplier = 1000; + + exponent = two_complement_to_int(data->vout_mode, 5, 0x1f); + mantissa = data->v_out; + + return (exponent > 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) : + sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); +} + +static const struct attribute_group cpr_4011_4mxx_group = { + .attrs = cpr_4011_4mxx_attributes, +}; + +static int cpr_4011_4mxx_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct cpr_4011_4mxx_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(struct cpr_4011_4mxx_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, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &cpr_4011_4mxx_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &cpr_4011_4mxx_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int cpr_4011_4mxx_remove(struct i2c_client *client) +{ + struct cpr_4011_4mxx_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &cpr_4011_4mxx_group); + kfree(data); + + return 0; +} + +static const struct i2c_device_id cpr_4011_4mxx_id[] = { + { "cpr_4011_4mxx", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, cpr_4011_4mxx_id); + +static struct i2c_driver cpr_4011_4mxx_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "cpr_4011_4mxx", + }, + .probe = cpr_4011_4mxx_probe, + .remove = cpr_4011_4mxx_remove, + .id_table = cpr_4011_4mxx_id, + .address_list = normal_i2c, +}; + +static int cpr_4011_4mxx_read_byte(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int cpr_4011_4mxx_read_word(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_data(client, reg); +} + +static int cpr_4011_4mxx_write_word(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_word_data(client, reg, value); +} + +struct reg_data_byte { + u8 reg; + u8 *value; +}; + +struct reg_data_word { + u8 reg; + u16 *value; +}; + +static struct cpr_4011_4mxx_data *cpr_4011_4mxx_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpr_4011_4mxx_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int i, status; + struct reg_data_byte regs_byte[] = { {0x20, &data->vout_mode}, + {0x81, &data->fan_fault}}; + struct reg_data_word regs_word[] = { {0x88, &data->v_in}, + {0x8b, &data->v_out}, + {0x89, &data->i_in}, + {0x8c, &data->i_out}, + {0x96, &data->p_out}, + {0x97, &data->p_in}, + {0x8d, &(data->temp_input[0])}, + {0x8e, &(data->temp_input[1])}, + {0x3b, &(data->fan_duty_cycle[0])}, + {0x3c, &(data->fan_duty_cycle[1])}, + {0x90, &(data->fan_speed[0])}, + {0x91, &(data->fan_speed[1])}}; + + dev_dbg(&client->dev, "Starting cpr_4011_4mxx update\n"); + + /* Read byte data */ + for (i = 0; i < ARRAY_SIZE(regs_byte); i++) { + status = cpr_4011_4mxx_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; + } + } + + /* Read word data */ + for (i = 0; i < ARRAY_SIZE(regs_word); i++) { + status = cpr_4011_4mxx_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; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init cpr_4011_4mxx_init(void) +{ + return i2c_add_driver(&cpr_4011_4mxx_driver); +} + +static void __exit cpr_4011_4mxx_exit(void) +{ + i2c_del_driver(&cpr_4011_4mxx_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("CPR_4011_4MXX driver"); +MODULE_LICENSE("GPL"); + +module_init(cpr_4011_4mxx_init); +module_exit(cpr_4011_4mxx_exit); diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/i2c-mux-accton_as5712_54x_cpld.c b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/i2c-mux-accton_as5712_54x_cpld.c new file mode 100755 index 000000000000..d63cb14b5a67 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/i2c-mux-accton_as5712_54x_cpld.c @@ -0,0 +1,1517 @@ +/* + * I2C multiplexer + * + * Copyright (C) Brandon Chuang + * + * This module supports the accton cpld that hold the channel select + * mechanism for other i2c slave devices, such as SFP. + * This includes the: + * Accton as5712_54x CPLD1/CPLD2/CPLD3 + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#if 0 +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I2C_RW_RETRY_COUNT 10 +#define I2C_RW_RETRY_INTERVAL 60 /* ms */ + +#define NUM_OF_CPLD1_CHANS 0x0 +#define NUM_OF_CPLD2_CHANS 0x18 +#define NUM_OF_CPLD3_CHANS 0x1E +#define CPLD_CHANNEL_SELECT_REG 0x2 +#define CPLD_DESELECT_CHANNEL 0xFF + +#if 0 +#define NUM_OF_ALL_CPLD_CHANS (NUM_OF_CPLD2_CHANS + NUM_OF_CPLD3_CHANS) +#endif + +#define ACCTON_I2C_CPLD_MUX_MAX_NCHANS NUM_OF_CPLD3_CHANS + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +enum cpld_mux_type { + as5712_54x_cpld2, + as5712_54x_cpld3, + as5712_54x_cpld1 +}; + +struct as5712_54x_cpld_data { + enum cpld_mux_type type; + struct i2c_adapter *virt_adaps[ACCTON_I2C_CPLD_MUX_MAX_NCHANS]; + u8 last_chan; /* last register value */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + struct i2c_client *client; + struct i2c_mux_core *muxc; +#endif + struct device *hwmon_dev; + struct mutex update_lock; +}; + +#if 0 +/* The mapping table between mux index and adapter index + array index : the mux index + the content : adapter index + */ +static int mux_adap_map[NUM_OF_ALL_CPLD_CHANS]; +#endif + +struct chip_desc { + u8 nchans; + u8 deselectChan; +}; + +/* Provide specs for the PCA954x types we know about */ +static const struct chip_desc chips[] = { + [as5712_54x_cpld1] = { + .nchans = NUM_OF_CPLD1_CHANS, + .deselectChan = CPLD_DESELECT_CHANNEL, + }, + [as5712_54x_cpld2] = { + .nchans = NUM_OF_CPLD2_CHANS, + .deselectChan = CPLD_DESELECT_CHANNEL, + }, + [as5712_54x_cpld3] = { + .nchans = NUM_OF_CPLD3_CHANS, + .deselectChan = CPLD_DESELECT_CHANNEL, + } +}; + +static const struct i2c_device_id as5712_54x_cpld_mux_id[] = { + { "as5712_54x_cpld1", as5712_54x_cpld1 }, + { "as5712_54x_cpld2", as5712_54x_cpld2 }, + { "as5712_54x_cpld3", as5712_54x_cpld3 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, as5712_54x_cpld_mux_id); + +#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index +#define TRANSCEIVER_TXDISABLE_ATTR_ID(index) MODULE_TXDISABLE_##index +#define TRANSCEIVER_RXLOS_ATTR_ID(index) MODULE_RXLOS_##index +#define TRANSCEIVER_TXFAULT_ATTR_ID(index) MODULE_TXFAULT_##index +#define TRANSCEIVER_LPMODE_ATTR_ID(index) MODULE_LPMODE_##index +#define TRANSCEIVER_RESET_ATTR_ID(index) MODULE_RESET_##index + +enum as5712_54x_cpld1_sysfs_attributes { + CPLD_VERSION, + ACCESS, + MODULE_PRESENT_ALL, + MODULE_RXLOS_ALL, + /* transceiver attributes */ + TRANSCEIVER_PRESENT_ATTR_ID(1), + TRANSCEIVER_PRESENT_ATTR_ID(2), + TRANSCEIVER_PRESENT_ATTR_ID(3), + TRANSCEIVER_PRESENT_ATTR_ID(4), + TRANSCEIVER_PRESENT_ATTR_ID(5), + TRANSCEIVER_PRESENT_ATTR_ID(6), + TRANSCEIVER_PRESENT_ATTR_ID(7), + TRANSCEIVER_PRESENT_ATTR_ID(8), + TRANSCEIVER_PRESENT_ATTR_ID(9), + TRANSCEIVER_PRESENT_ATTR_ID(10), + TRANSCEIVER_PRESENT_ATTR_ID(11), + TRANSCEIVER_PRESENT_ATTR_ID(12), + TRANSCEIVER_PRESENT_ATTR_ID(13), + TRANSCEIVER_PRESENT_ATTR_ID(14), + TRANSCEIVER_PRESENT_ATTR_ID(15), + TRANSCEIVER_PRESENT_ATTR_ID(16), + TRANSCEIVER_PRESENT_ATTR_ID(17), + TRANSCEIVER_PRESENT_ATTR_ID(18), + TRANSCEIVER_PRESENT_ATTR_ID(19), + TRANSCEIVER_PRESENT_ATTR_ID(20), + TRANSCEIVER_PRESENT_ATTR_ID(21), + TRANSCEIVER_PRESENT_ATTR_ID(22), + TRANSCEIVER_PRESENT_ATTR_ID(23), + TRANSCEIVER_PRESENT_ATTR_ID(24), + TRANSCEIVER_PRESENT_ATTR_ID(25), + TRANSCEIVER_PRESENT_ATTR_ID(26), + TRANSCEIVER_PRESENT_ATTR_ID(27), + TRANSCEIVER_PRESENT_ATTR_ID(28), + TRANSCEIVER_PRESENT_ATTR_ID(29), + TRANSCEIVER_PRESENT_ATTR_ID(30), + TRANSCEIVER_PRESENT_ATTR_ID(31), + TRANSCEIVER_PRESENT_ATTR_ID(32), + TRANSCEIVER_PRESENT_ATTR_ID(33), + TRANSCEIVER_PRESENT_ATTR_ID(34), + TRANSCEIVER_PRESENT_ATTR_ID(35), + TRANSCEIVER_PRESENT_ATTR_ID(36), + TRANSCEIVER_PRESENT_ATTR_ID(37), + TRANSCEIVER_PRESENT_ATTR_ID(38), + TRANSCEIVER_PRESENT_ATTR_ID(39), + TRANSCEIVER_PRESENT_ATTR_ID(40), + TRANSCEIVER_PRESENT_ATTR_ID(41), + TRANSCEIVER_PRESENT_ATTR_ID(42), + TRANSCEIVER_PRESENT_ATTR_ID(43), + TRANSCEIVER_PRESENT_ATTR_ID(44), + TRANSCEIVER_PRESENT_ATTR_ID(45), + TRANSCEIVER_PRESENT_ATTR_ID(46), + TRANSCEIVER_PRESENT_ATTR_ID(47), + TRANSCEIVER_PRESENT_ATTR_ID(48), + TRANSCEIVER_PRESENT_ATTR_ID(49), + TRANSCEIVER_PRESENT_ATTR_ID(50), + TRANSCEIVER_PRESENT_ATTR_ID(51), + TRANSCEIVER_PRESENT_ATTR_ID(52), + TRANSCEIVER_PRESENT_ATTR_ID(53), + TRANSCEIVER_PRESENT_ATTR_ID(54), + TRANSCEIVER_TXDISABLE_ATTR_ID(1), + TRANSCEIVER_TXDISABLE_ATTR_ID(2), + TRANSCEIVER_TXDISABLE_ATTR_ID(3), + TRANSCEIVER_TXDISABLE_ATTR_ID(4), + TRANSCEIVER_TXDISABLE_ATTR_ID(5), + TRANSCEIVER_TXDISABLE_ATTR_ID(6), + TRANSCEIVER_TXDISABLE_ATTR_ID(7), + TRANSCEIVER_TXDISABLE_ATTR_ID(8), + TRANSCEIVER_TXDISABLE_ATTR_ID(9), + TRANSCEIVER_TXDISABLE_ATTR_ID(10), + TRANSCEIVER_TXDISABLE_ATTR_ID(11), + TRANSCEIVER_TXDISABLE_ATTR_ID(12), + TRANSCEIVER_TXDISABLE_ATTR_ID(13), + TRANSCEIVER_TXDISABLE_ATTR_ID(14), + TRANSCEIVER_TXDISABLE_ATTR_ID(15), + TRANSCEIVER_TXDISABLE_ATTR_ID(16), + TRANSCEIVER_TXDISABLE_ATTR_ID(17), + TRANSCEIVER_TXDISABLE_ATTR_ID(18), + TRANSCEIVER_TXDISABLE_ATTR_ID(19), + TRANSCEIVER_TXDISABLE_ATTR_ID(20), + TRANSCEIVER_TXDISABLE_ATTR_ID(21), + TRANSCEIVER_TXDISABLE_ATTR_ID(22), + TRANSCEIVER_TXDISABLE_ATTR_ID(23), + TRANSCEIVER_TXDISABLE_ATTR_ID(24), + TRANSCEIVER_TXDISABLE_ATTR_ID(25), + TRANSCEIVER_TXDISABLE_ATTR_ID(26), + TRANSCEIVER_TXDISABLE_ATTR_ID(27), + TRANSCEIVER_TXDISABLE_ATTR_ID(28), + TRANSCEIVER_TXDISABLE_ATTR_ID(29), + TRANSCEIVER_TXDISABLE_ATTR_ID(30), + TRANSCEIVER_TXDISABLE_ATTR_ID(31), + TRANSCEIVER_TXDISABLE_ATTR_ID(32), + TRANSCEIVER_TXDISABLE_ATTR_ID(33), + TRANSCEIVER_TXDISABLE_ATTR_ID(34), + TRANSCEIVER_TXDISABLE_ATTR_ID(35), + TRANSCEIVER_TXDISABLE_ATTR_ID(36), + TRANSCEIVER_TXDISABLE_ATTR_ID(37), + TRANSCEIVER_TXDISABLE_ATTR_ID(38), + TRANSCEIVER_TXDISABLE_ATTR_ID(39), + TRANSCEIVER_TXDISABLE_ATTR_ID(40), + TRANSCEIVER_TXDISABLE_ATTR_ID(41), + TRANSCEIVER_TXDISABLE_ATTR_ID(42), + TRANSCEIVER_TXDISABLE_ATTR_ID(43), + TRANSCEIVER_TXDISABLE_ATTR_ID(44), + TRANSCEIVER_TXDISABLE_ATTR_ID(45), + TRANSCEIVER_TXDISABLE_ATTR_ID(46), + TRANSCEIVER_TXDISABLE_ATTR_ID(47), + TRANSCEIVER_TXDISABLE_ATTR_ID(48), + TRANSCEIVER_RXLOS_ATTR_ID(1), + TRANSCEIVER_RXLOS_ATTR_ID(2), + TRANSCEIVER_RXLOS_ATTR_ID(3), + TRANSCEIVER_RXLOS_ATTR_ID(4), + TRANSCEIVER_RXLOS_ATTR_ID(5), + TRANSCEIVER_RXLOS_ATTR_ID(6), + TRANSCEIVER_RXLOS_ATTR_ID(7), + TRANSCEIVER_RXLOS_ATTR_ID(8), + TRANSCEIVER_RXLOS_ATTR_ID(9), + TRANSCEIVER_RXLOS_ATTR_ID(10), + TRANSCEIVER_RXLOS_ATTR_ID(11), + TRANSCEIVER_RXLOS_ATTR_ID(12), + TRANSCEIVER_RXLOS_ATTR_ID(13), + TRANSCEIVER_RXLOS_ATTR_ID(14), + TRANSCEIVER_RXLOS_ATTR_ID(15), + TRANSCEIVER_RXLOS_ATTR_ID(16), + TRANSCEIVER_RXLOS_ATTR_ID(17), + TRANSCEIVER_RXLOS_ATTR_ID(18), + TRANSCEIVER_RXLOS_ATTR_ID(19), + TRANSCEIVER_RXLOS_ATTR_ID(20), + TRANSCEIVER_RXLOS_ATTR_ID(21), + TRANSCEIVER_RXLOS_ATTR_ID(22), + TRANSCEIVER_RXLOS_ATTR_ID(23), + TRANSCEIVER_RXLOS_ATTR_ID(24), + TRANSCEIVER_RXLOS_ATTR_ID(25), + TRANSCEIVER_RXLOS_ATTR_ID(26), + TRANSCEIVER_RXLOS_ATTR_ID(27), + TRANSCEIVER_RXLOS_ATTR_ID(28), + TRANSCEIVER_RXLOS_ATTR_ID(29), + TRANSCEIVER_RXLOS_ATTR_ID(30), + TRANSCEIVER_RXLOS_ATTR_ID(31), + TRANSCEIVER_RXLOS_ATTR_ID(32), + TRANSCEIVER_RXLOS_ATTR_ID(33), + TRANSCEIVER_RXLOS_ATTR_ID(34), + TRANSCEIVER_RXLOS_ATTR_ID(35), + TRANSCEIVER_RXLOS_ATTR_ID(36), + TRANSCEIVER_RXLOS_ATTR_ID(37), + TRANSCEIVER_RXLOS_ATTR_ID(38), + TRANSCEIVER_RXLOS_ATTR_ID(39), + TRANSCEIVER_RXLOS_ATTR_ID(40), + TRANSCEIVER_RXLOS_ATTR_ID(41), + TRANSCEIVER_RXLOS_ATTR_ID(42), + TRANSCEIVER_RXLOS_ATTR_ID(43), + TRANSCEIVER_RXLOS_ATTR_ID(44), + TRANSCEIVER_RXLOS_ATTR_ID(45), + TRANSCEIVER_RXLOS_ATTR_ID(46), + TRANSCEIVER_RXLOS_ATTR_ID(47), + TRANSCEIVER_RXLOS_ATTR_ID(48), + TRANSCEIVER_TXFAULT_ATTR_ID(1), + TRANSCEIVER_TXFAULT_ATTR_ID(2), + TRANSCEIVER_TXFAULT_ATTR_ID(3), + TRANSCEIVER_TXFAULT_ATTR_ID(4), + TRANSCEIVER_TXFAULT_ATTR_ID(5), + TRANSCEIVER_TXFAULT_ATTR_ID(6), + TRANSCEIVER_TXFAULT_ATTR_ID(7), + TRANSCEIVER_TXFAULT_ATTR_ID(8), + TRANSCEIVER_TXFAULT_ATTR_ID(9), + TRANSCEIVER_TXFAULT_ATTR_ID(10), + TRANSCEIVER_TXFAULT_ATTR_ID(11), + TRANSCEIVER_TXFAULT_ATTR_ID(12), + TRANSCEIVER_TXFAULT_ATTR_ID(13), + TRANSCEIVER_TXFAULT_ATTR_ID(14), + TRANSCEIVER_TXFAULT_ATTR_ID(15), + TRANSCEIVER_TXFAULT_ATTR_ID(16), + TRANSCEIVER_TXFAULT_ATTR_ID(17), + TRANSCEIVER_TXFAULT_ATTR_ID(18), + TRANSCEIVER_TXFAULT_ATTR_ID(19), + TRANSCEIVER_TXFAULT_ATTR_ID(20), + TRANSCEIVER_TXFAULT_ATTR_ID(21), + TRANSCEIVER_TXFAULT_ATTR_ID(22), + TRANSCEIVER_TXFAULT_ATTR_ID(23), + TRANSCEIVER_TXFAULT_ATTR_ID(24), + TRANSCEIVER_TXFAULT_ATTR_ID(25), + TRANSCEIVER_TXFAULT_ATTR_ID(26), + TRANSCEIVER_TXFAULT_ATTR_ID(27), + TRANSCEIVER_TXFAULT_ATTR_ID(28), + TRANSCEIVER_TXFAULT_ATTR_ID(29), + TRANSCEIVER_TXFAULT_ATTR_ID(30), + TRANSCEIVER_TXFAULT_ATTR_ID(31), + TRANSCEIVER_TXFAULT_ATTR_ID(32), + TRANSCEIVER_TXFAULT_ATTR_ID(33), + TRANSCEIVER_TXFAULT_ATTR_ID(34), + TRANSCEIVER_TXFAULT_ATTR_ID(35), + TRANSCEIVER_TXFAULT_ATTR_ID(36), + TRANSCEIVER_TXFAULT_ATTR_ID(37), + TRANSCEIVER_TXFAULT_ATTR_ID(38), + TRANSCEIVER_TXFAULT_ATTR_ID(39), + TRANSCEIVER_TXFAULT_ATTR_ID(40), + TRANSCEIVER_TXFAULT_ATTR_ID(41), + TRANSCEIVER_TXFAULT_ATTR_ID(42), + TRANSCEIVER_TXFAULT_ATTR_ID(43), + TRANSCEIVER_TXFAULT_ATTR_ID(44), + TRANSCEIVER_TXFAULT_ATTR_ID(45), + TRANSCEIVER_TXFAULT_ATTR_ID(46), + TRANSCEIVER_TXFAULT_ATTR_ID(47), + TRANSCEIVER_TXFAULT_ATTR_ID(48), + TRANSCEIVER_LPMODE_ATTR_ID(49), + TRANSCEIVER_LPMODE_ATTR_ID(50), + TRANSCEIVER_LPMODE_ATTR_ID(51), + TRANSCEIVER_LPMODE_ATTR_ID(52), + TRANSCEIVER_LPMODE_ATTR_ID(53), + TRANSCEIVER_LPMODE_ATTR_ID(54), + TRANSCEIVER_RESET_ATTR_ID(49), + TRANSCEIVER_RESET_ATTR_ID(50), + TRANSCEIVER_RESET_ATTR_ID(51), + TRANSCEIVER_RESET_ATTR_ID(52), + TRANSCEIVER_RESET_ATTR_ID(53), + TRANSCEIVER_RESET_ATTR_ID(54), +}; + +/* sysfs attributes for hwmon + */ +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_present_all(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_rxlos_all(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t set_lp_mode(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t set_mode_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t show_version(struct device *dev, struct device_attribute *da, + char *buf); +static int as5712_54x_cpld_read_internal(struct i2c_client *client, u8 reg); +static int as5712_54x_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value); + +/* transceiver attributes */ +#define DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, show_status, NULL, MODULE_PRESENT_##index) +#define DECLARE_TRANSCEIVER_PRESENT_ATTR(index) &sensor_dev_attr_module_present_##index.dev_attr.attr + +#define DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_tx_disable_##index, S_IRUGO | S_IWUSR, show_status, set_tx_disable, MODULE_TXDISABLE_##index); \ + static SENSOR_DEVICE_ATTR(module_rx_los_##index, S_IRUGO, show_status, NULL, MODULE_RXLOS_##index); \ + static SENSOR_DEVICE_ATTR(module_tx_fault_##index, S_IRUGO, show_status, NULL, MODULE_TXFAULT_##index) + +#define DECLARE_SFP_TRANSCEIVER_ATTR(index) \ + &sensor_dev_attr_module_tx_disable_##index.dev_attr.attr, \ + &sensor_dev_attr_module_rx_los_##index.dev_attr.attr, \ + &sensor_dev_attr_module_tx_fault_##index.dev_attr.attr + +#define DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_lp_mode_##index, S_IRUGO | S_IWUSR, show_status, set_lp_mode, MODULE_LPMODE_##index); \ + static SENSOR_DEVICE_ATTR(module_reset_##index, S_IWUSR | S_IRUGO, show_status, set_mode_reset, MODULE_RESET_##index) + +#define DECLARE_QSFP_TRANSCEIVER_ATTR(index) \ + &sensor_dev_attr_module_lp_mode_##index.dev_attr.attr, \ + &sensor_dev_attr_module_reset_##index.dev_attr.attr + + +static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_version, NULL, CPLD_VERSION); +static SENSOR_DEVICE_ATTR(access, S_IWUSR, NULL, access, ACCESS); +/* transceiver attributes */ +static SENSOR_DEVICE_ATTR(module_present_all, S_IRUGO, show_present_all, NULL, MODULE_PRESENT_ALL); +static SENSOR_DEVICE_ATTR(module_rx_los_all, S_IRUGO, show_rxlos_all, NULL, MODULE_RXLOS_ALL); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(1); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(2); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(3); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(4); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(5); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(6); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(7); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(8); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(9); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(10); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(11); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(12); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(13); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(14); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(15); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(16); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(17); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(18); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(19); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(20); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(21); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(22); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(23); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(24); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(25); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(26); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(27); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(28); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(29); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(30); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(31); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(32); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(33); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(34); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(35); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(36); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(37); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(38); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(39); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(40); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(41); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(42); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(43); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(44); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(45); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(46); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(47); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(48); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(49); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(50); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(51); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(52); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(53); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(54); + +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(1); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(2); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(3); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(4); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(5); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(6); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(7); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(8); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(9); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(10); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(11); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(12); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(13); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(14); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(15); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(16); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(17); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(18); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(19); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(20); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(21); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(22); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(23); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(24); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(25); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(26); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(27); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(28); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(29); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(30); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(31); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(32); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(33); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(34); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(35); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(36); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(37); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(38); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(39); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(40); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(41); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(42); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(43); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(44); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(45); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(46); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(47); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(48); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(49); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(50); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(51); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(52); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(53); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(54); + +static struct attribute *as5712_54x_cpld1_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + NULL +}; + +static const struct attribute_group as5712_54x_cpld1_group = { + .attrs = as5712_54x_cpld1_attributes, +}; + +static struct attribute *as5712_54x_cpld2_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + /* transceiver attributes */ + &sensor_dev_attr_module_present_all.dev_attr.attr, + &sensor_dev_attr_module_rx_los_all.dev_attr.attr, + DECLARE_TRANSCEIVER_PRESENT_ATTR(1), + DECLARE_TRANSCEIVER_PRESENT_ATTR(2), + DECLARE_TRANSCEIVER_PRESENT_ATTR(3), + DECLARE_TRANSCEIVER_PRESENT_ATTR(4), + DECLARE_TRANSCEIVER_PRESENT_ATTR(5), + DECLARE_TRANSCEIVER_PRESENT_ATTR(6), + DECLARE_TRANSCEIVER_PRESENT_ATTR(7), + DECLARE_TRANSCEIVER_PRESENT_ATTR(8), + DECLARE_TRANSCEIVER_PRESENT_ATTR(9), + DECLARE_TRANSCEIVER_PRESENT_ATTR(10), + DECLARE_TRANSCEIVER_PRESENT_ATTR(11), + DECLARE_TRANSCEIVER_PRESENT_ATTR(12), + DECLARE_TRANSCEIVER_PRESENT_ATTR(13), + DECLARE_TRANSCEIVER_PRESENT_ATTR(14), + DECLARE_TRANSCEIVER_PRESENT_ATTR(15), + DECLARE_TRANSCEIVER_PRESENT_ATTR(16), + DECLARE_TRANSCEIVER_PRESENT_ATTR(17), + DECLARE_TRANSCEIVER_PRESENT_ATTR(18), + DECLARE_TRANSCEIVER_PRESENT_ATTR(19), + DECLARE_TRANSCEIVER_PRESENT_ATTR(20), + DECLARE_TRANSCEIVER_PRESENT_ATTR(21), + DECLARE_TRANSCEIVER_PRESENT_ATTR(22), + DECLARE_TRANSCEIVER_PRESENT_ATTR(23), + DECLARE_TRANSCEIVER_PRESENT_ATTR(24), + DECLARE_SFP_TRANSCEIVER_ATTR(1), + DECLARE_SFP_TRANSCEIVER_ATTR(2), + DECLARE_SFP_TRANSCEIVER_ATTR(3), + DECLARE_SFP_TRANSCEIVER_ATTR(4), + DECLARE_SFP_TRANSCEIVER_ATTR(5), + DECLARE_SFP_TRANSCEIVER_ATTR(6), + DECLARE_SFP_TRANSCEIVER_ATTR(7), + DECLARE_SFP_TRANSCEIVER_ATTR(8), + DECLARE_SFP_TRANSCEIVER_ATTR(9), + DECLARE_SFP_TRANSCEIVER_ATTR(10), + DECLARE_SFP_TRANSCEIVER_ATTR(11), + DECLARE_SFP_TRANSCEIVER_ATTR(12), + DECLARE_SFP_TRANSCEIVER_ATTR(13), + DECLARE_SFP_TRANSCEIVER_ATTR(14), + DECLARE_SFP_TRANSCEIVER_ATTR(15), + DECLARE_SFP_TRANSCEIVER_ATTR(16), + DECLARE_SFP_TRANSCEIVER_ATTR(17), + DECLARE_SFP_TRANSCEIVER_ATTR(18), + DECLARE_SFP_TRANSCEIVER_ATTR(19), + DECLARE_SFP_TRANSCEIVER_ATTR(20), + DECLARE_SFP_TRANSCEIVER_ATTR(21), + DECLARE_SFP_TRANSCEIVER_ATTR(22), + DECLARE_SFP_TRANSCEIVER_ATTR(23), + DECLARE_SFP_TRANSCEIVER_ATTR(24), + NULL +}; + +static const struct attribute_group as5712_54x_cpld2_group = { + .attrs = as5712_54x_cpld2_attributes, +}; + +static struct attribute *as5712_54x_cpld3_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + /* transceiver attributes */ + &sensor_dev_attr_module_present_all.dev_attr.attr, + &sensor_dev_attr_module_rx_los_all.dev_attr.attr, + DECLARE_TRANSCEIVER_PRESENT_ATTR(25), + DECLARE_TRANSCEIVER_PRESENT_ATTR(26), + DECLARE_TRANSCEIVER_PRESENT_ATTR(27), + DECLARE_TRANSCEIVER_PRESENT_ATTR(28), + DECLARE_TRANSCEIVER_PRESENT_ATTR(29), + DECLARE_TRANSCEIVER_PRESENT_ATTR(30), + DECLARE_TRANSCEIVER_PRESENT_ATTR(31), + DECLARE_TRANSCEIVER_PRESENT_ATTR(32), + DECLARE_TRANSCEIVER_PRESENT_ATTR(33), + DECLARE_TRANSCEIVER_PRESENT_ATTR(34), + DECLARE_TRANSCEIVER_PRESENT_ATTR(35), + DECLARE_TRANSCEIVER_PRESENT_ATTR(36), + DECLARE_TRANSCEIVER_PRESENT_ATTR(37), + DECLARE_TRANSCEIVER_PRESENT_ATTR(38), + DECLARE_TRANSCEIVER_PRESENT_ATTR(39), + DECLARE_TRANSCEIVER_PRESENT_ATTR(40), + DECLARE_TRANSCEIVER_PRESENT_ATTR(41), + DECLARE_TRANSCEIVER_PRESENT_ATTR(42), + DECLARE_TRANSCEIVER_PRESENT_ATTR(43), + DECLARE_TRANSCEIVER_PRESENT_ATTR(44), + DECLARE_TRANSCEIVER_PRESENT_ATTR(45), + DECLARE_TRANSCEIVER_PRESENT_ATTR(46), + DECLARE_TRANSCEIVER_PRESENT_ATTR(47), + DECLARE_TRANSCEIVER_PRESENT_ATTR(48), + DECLARE_TRANSCEIVER_PRESENT_ATTR(49), + DECLARE_TRANSCEIVER_PRESENT_ATTR(50), + DECLARE_TRANSCEIVER_PRESENT_ATTR(51), + DECLARE_TRANSCEIVER_PRESENT_ATTR(52), + DECLARE_TRANSCEIVER_PRESENT_ATTR(53), + DECLARE_TRANSCEIVER_PRESENT_ATTR(54), + DECLARE_SFP_TRANSCEIVER_ATTR(25), + DECLARE_SFP_TRANSCEIVER_ATTR(26), + DECLARE_SFP_TRANSCEIVER_ATTR(27), + DECLARE_SFP_TRANSCEIVER_ATTR(28), + DECLARE_SFP_TRANSCEIVER_ATTR(29), + DECLARE_SFP_TRANSCEIVER_ATTR(30), + DECLARE_SFP_TRANSCEIVER_ATTR(31), + DECLARE_SFP_TRANSCEIVER_ATTR(32), + DECLARE_SFP_TRANSCEIVER_ATTR(33), + DECLARE_SFP_TRANSCEIVER_ATTR(34), + DECLARE_SFP_TRANSCEIVER_ATTR(35), + DECLARE_SFP_TRANSCEIVER_ATTR(36), + DECLARE_SFP_TRANSCEIVER_ATTR(37), + DECLARE_SFP_TRANSCEIVER_ATTR(38), + DECLARE_SFP_TRANSCEIVER_ATTR(39), + DECLARE_SFP_TRANSCEIVER_ATTR(40), + DECLARE_SFP_TRANSCEIVER_ATTR(41), + DECLARE_SFP_TRANSCEIVER_ATTR(42), + DECLARE_SFP_TRANSCEIVER_ATTR(43), + DECLARE_SFP_TRANSCEIVER_ATTR(44), + DECLARE_SFP_TRANSCEIVER_ATTR(45), + DECLARE_SFP_TRANSCEIVER_ATTR(46), + DECLARE_SFP_TRANSCEIVER_ATTR(47), + DECLARE_SFP_TRANSCEIVER_ATTR(48), + DECLARE_QSFP_TRANSCEIVER_ATTR(49), + DECLARE_QSFP_TRANSCEIVER_ATTR(50), + DECLARE_QSFP_TRANSCEIVER_ATTR(51), + DECLARE_QSFP_TRANSCEIVER_ATTR(52), + DECLARE_QSFP_TRANSCEIVER_ATTR(53), + DECLARE_QSFP_TRANSCEIVER_ATTR(54), + NULL +}; + +static const struct attribute_group as5712_54x_cpld3_group = { + .attrs = as5712_54x_cpld3_attributes, +}; + +static ssize_t show_present_all(struct device *dev, struct device_attribute *da, + char *buf) +{ + int i, status, num_regs = 0; + u8 values[4] = {0}; + u8 regs[] = {0x6, 0x7, 0x8, 0x14}; + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_cpld_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + num_regs = (data->type == as5712_54x_cpld2) ? 3 : 4; + + for (i = 0; i < num_regs; i++) { + status = as5712_54x_cpld_read_internal(client, regs[i]); + + if (status < 0) { + goto exit; + } + + values[i] = ~(u8)status; + } + + mutex_unlock(&data->update_lock); + + /* Return values 1 -> 54 in order */ + if (data->type == as5712_54x_cpld2) { + status = sprintf(buf, "%.2x %.2x %.2x\n", + values[0], values[1], values[2]); + } + else { /* as5712_54x_cpld3 */ + values[3] &= 0x3F; + status = sprintf(buf, "%.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], values[3]); + } + + return status; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_rxlos_all(struct device *dev, struct device_attribute *da, + char *buf) +{ + int i, status; + u8 values[3] = {0}; + u8 regs[] = {0xF, 0x10, 0x11}; + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_cpld_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + status = as5712_54x_cpld_read_internal(client, regs[i]); + + if (status < 0) { + goto exit; + } + + values[i] = (u8)status; + } + + mutex_unlock(&data->update_lock); + + /* Return values 1 -> 24 in order */ + return sprintf(buf, "%.2x %.2x %.2x\n", values[0], values[1], values[2]); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_cpld_data *data = i2c_get_clientdata(client); + int status = 0; + u8 reg = 0, mask = 0, revert = 0; + + switch (attr->index) { + case MODULE_PRESENT_1 ... MODULE_PRESENT_8: + reg = 0x6; + mask = 0x1 << (attr->index - MODULE_PRESENT_1); + break; + case MODULE_PRESENT_9 ... MODULE_PRESENT_16: + reg = 0x7; + mask = 0x1 << (attr->index - MODULE_PRESENT_9); + break; + case MODULE_PRESENT_17 ... MODULE_PRESENT_24: + reg = 0x8; + mask = 0x1 << (attr->index - MODULE_PRESENT_17); + break; + case MODULE_PRESENT_25 ... MODULE_PRESENT_32: + reg = 0x6; + mask = 0x1 << (attr->index - MODULE_PRESENT_25); + break; + case MODULE_PRESENT_33 ... MODULE_PRESENT_40: + reg = 0x7; + mask = 0x1 << (attr->index - MODULE_PRESENT_33); + break; + case MODULE_PRESENT_41 ... MODULE_PRESENT_48: + reg = 0x8; + mask = 0x1 << (attr->index - MODULE_PRESENT_41); + break; + case MODULE_PRESENT_49: + reg = 0x14; + mask = 0x1; + break; + case MODULE_PRESENT_50: + reg = 0x14; + mask = 0x4; + break; + case MODULE_PRESENT_51: + reg = 0x14; + mask = 0x10; + break; + case MODULE_PRESENT_52: + reg = 0x14; + mask = 0x2; + break; + case MODULE_PRESENT_53: + reg = 0x14; + mask = 0x8; + break; + case MODULE_PRESENT_54: + reg = 0x14; + mask = 0x20; + break; + case MODULE_TXFAULT_1 ... MODULE_TXFAULT_8: + reg = 0x9; + mask = 0x1 << (attr->index - MODULE_TXFAULT_1); + break; + case MODULE_TXFAULT_9 ... MODULE_TXFAULT_16: + reg = 0xA; + mask = 0x1 << (attr->index - MODULE_TXFAULT_9); + break; + case MODULE_TXFAULT_17 ... MODULE_TXFAULT_24: + reg = 0xB; + mask = 0x1 << (attr->index - MODULE_TXFAULT_17); + break; + case MODULE_TXFAULT_25 ... MODULE_TXFAULT_32: + reg = 0x9; + mask = 0x1 << (attr->index - MODULE_TXFAULT_25); + break; + case MODULE_TXFAULT_33 ... MODULE_TXFAULT_40: + reg = 0xA; + mask = 0x1 << (attr->index - MODULE_TXFAULT_33); + break; + case MODULE_TXFAULT_41 ... MODULE_TXFAULT_48: + reg = 0xB; + mask = 0x1 << (attr->index - MODULE_TXFAULT_41); + break; + case MODULE_TXDISABLE_1 ... MODULE_TXDISABLE_8: + reg = 0xC; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_1); + break; + case MODULE_TXDISABLE_9 ... MODULE_TXDISABLE_16: + reg = 0xD; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_9); + break; + case MODULE_TXDISABLE_17 ... MODULE_TXDISABLE_24: + reg = 0xE; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_17); + break; + case MODULE_TXDISABLE_25 ... MODULE_TXDISABLE_32: + reg = 0xC; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_25); + break; + case MODULE_TXDISABLE_33 ... MODULE_TXDISABLE_40: + reg = 0xD; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_33); + break; + case MODULE_TXDISABLE_41 ... MODULE_TXDISABLE_48: + reg = 0xE; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_41); + break; + case MODULE_RXLOS_1 ... MODULE_RXLOS_8: + reg = 0xF; + mask = 0x1 << (attr->index - MODULE_RXLOS_1); + break; + case MODULE_RXLOS_9 ... MODULE_RXLOS_16: + reg = 0x10; + mask = 0x1 << (attr->index - MODULE_RXLOS_9); + break; + case MODULE_RXLOS_17 ... MODULE_RXLOS_24: + reg = 0x11; + mask = 0x1 << (attr->index - MODULE_RXLOS_17); + break; + case MODULE_RXLOS_25 ... MODULE_RXLOS_32: + reg = 0xF; + mask = 0x1 << (attr->index - MODULE_RXLOS_25); + break; + case MODULE_RXLOS_33 ... MODULE_RXLOS_40: + reg = 0x10; + mask = 0x1 << (attr->index - MODULE_RXLOS_33); + break; + case MODULE_RXLOS_41 ... MODULE_RXLOS_48: + reg = 0x11; + mask = 0x1 << (attr->index - MODULE_RXLOS_41); + break; + case MODULE_LPMODE_49 ... MODULE_LPMODE_54: + reg = 0x16; + mask = 0x1 << (attr->index - MODULE_LPMODE_49); + break; + case MODULE_RESET_49 ... MODULE_RESET_54: + reg = 0x15; + mask = 0x1 << (attr->index - MODULE_RESET_49); + break; + default: + return 0; + } + + if (attr->index >= MODULE_PRESENT_1 && attr->index <= MODULE_PRESENT_54) { + revert = 1; + } + + mutex_lock(&data->update_lock); + status = as5712_54x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", revert ? !(status & mask) : !!(status & mask)); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_cpld_data *data = i2c_get_clientdata(client); + long disable; + int status; + u8 reg = 0, mask = 0; + + status = kstrtol(buf, 10, &disable); + if (status) { + return status; + } + + switch (attr->index) { + case MODULE_TXDISABLE_1 ... MODULE_TXDISABLE_8: + reg = 0xC; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_1); + break; + case MODULE_TXDISABLE_9 ... MODULE_TXDISABLE_16: + reg = 0xD; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_9); + break; + case MODULE_TXDISABLE_17 ... MODULE_TXDISABLE_24: + reg = 0xE; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_17); + break; + case MODULE_TXDISABLE_25 ... MODULE_TXDISABLE_32: + reg = 0xC; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_25); + break; + case MODULE_TXDISABLE_33 ... MODULE_TXDISABLE_40: + reg = 0xD; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_33); + break; + case MODULE_TXDISABLE_41 ... MODULE_TXDISABLE_48: + reg = 0xE; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_41); + break; + default: + return 0; + } + + /* Read current status */ + mutex_lock(&data->update_lock); + status = as5712_54x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + + /* Update tx_disable status */ + if (disable) { + status |= mask; + } + else { + status &= ~mask; + } + + status = as5712_54x_cpld_write_internal(client, reg, status); + if (unlikely(status < 0)) { + goto exit; + } + + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t set_lp_mode(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_cpld_data *data = i2c_get_clientdata(client); + long on; + int status= -ENOENT; + u8 reg = 0x16, mask = 0; + + if(attr->index < MODULE_LPMODE_49 || attr->index > MODULE_LPMODE_54) + return status; + + status = kstrtol(buf, 10, &on); + if (status) { + return status; + } + + /* Read current status */ + mutex_lock(&data->update_lock); + status = as5712_54x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + + mask = 0x1 << (attr->index - MODULE_LPMODE_49); + + /* Update lp_mode status */ + if (on) { + status |= mask; + } + else { + status &= ~mask; + } + + status = as5712_54x_cpld_write_internal(client, reg, status); + if (unlikely(status < 0)) { + goto exit; + } + + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; + +} + +static ssize_t set_mode_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_cpld_data *data = i2c_get_clientdata(client); + long on; + int status= -ENOENT; + u8 reg = 0x15, mask = 0; + + if(attr->index < MODULE_RESET_49 || attr->index > MODULE_RESET_54) + return status; + + status = kstrtol(buf, 10, &on); + if (status) { + return status; + } + + /* Read current status */ + mutex_lock(&data->update_lock); + status = as5712_54x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + + mask = 0x1 << (attr->index - MODULE_RESET_49); + + /* Update tx_disable status */ + if (on) { + status |= mask; + } + else { + status &= ~mask; + } + + status = as5712_54x_cpld_write_internal(client, reg, status); + if (unlikely(status < 0)) { + goto exit; + } + + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; + +} + +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int status; + u32 addr, val; + struct i2c_client *client = to_i2c_client(dev); + struct as5712_54x_cpld_data *data = i2c_get_clientdata(client); + + if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) { + return -EINVAL; + } + + if (addr > 0xFF || val > 0xFF) { + return -EINVAL; + } + + mutex_lock(&data->update_lock); + status = as5712_54x_cpld_write_internal(client, addr, val); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer() + for this as they will try to lock adapter a second time */ +static int as5712_54x_cpld_mux_reg_write(struct i2c_adapter *adap, + struct i2c_client *client, u8 val) +{ + unsigned long orig_jiffies; + unsigned short flags; + union i2c_smbus_data data; + int try; + s32 res = -EIO; + + data.byte = val; + flags = client->flags; + flags &= I2C_M_TEN | I2C_CLIENT_PEC; + + if (adap->algo->smbus_xfer) { + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (res = 0, try = 0; try <= adap->retries; try++) { + res = adap->algo->smbus_xfer(adap, client->addr, flags, + I2C_SMBUS_WRITE, CPLD_CHANNEL_SELECT_REG, + I2C_SMBUS_BYTE_DATA, &data); + if (res != -EAGAIN) + break; + if (time_after(jiffies, + orig_jiffies + adap->timeout)) + break; + } + } + + return res; + +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) +static int as5712_54x_cpld_mux_select_chan(struct i2c_mux_core *muxc, + u32 chan) +{ + struct as5712_54x_cpld_data *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + u8 regval; + int ret = 0; + regval = chan; + + /* Only select the channel if its different from the last channel */ + if (data->last_chan != regval) { + ret = as5712_54x_cpld_mux_reg_write(muxc->parent, client, regval); + data->last_chan = regval; + } + + return ret; +} + +static int as5712_54x_cpld_mux_deselect_mux(struct i2c_mux_core *muxc, + u32 chan) +{ + struct as5712_54x_cpld_data *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + + /* Deselect active channel */ + data->last_chan = chips[data->type].deselectChan; + + return as5712_54x_cpld_mux_reg_write(muxc->parent, client, data->last_chan); +} +#else + +static int as5712_54x_cpld_mux_select_chan(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct as5712_54x_cpld_data *data = i2c_get_clientdata(client); + u8 regval; + int ret = 0; + regval = chan; + + /* Only select the channel if its different from the last channel */ + if (data->last_chan != regval) { + ret = as5712_54x_cpld_mux_reg_write(adap, client, regval); + data->last_chan = regval; + } + + return ret; +} + +static int as5712_54x_cpld_mux_deselect_mux(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct as5712_54x_cpld_data *data = i2c_get_clientdata(client); + + /* Deselect active channel */ + data->last_chan = chips[data->type].deselectChan; + + return as5712_54x_cpld_mux_reg_write(adap, client, data->last_chan); +} + +#endif /*#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)*/ + +static void as5712_54x_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void as5712_54x_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + + mutex_unlock(&list_lock); +} + +static ssize_t show_version(struct device *dev, struct device_attribute *attr, char *buf) +{ + int val = 0; + struct i2c_client *client = to_i2c_client(dev); + + val = i2c_smbus_read_byte_data(client, 0x1); + + if (val < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x1) err %d\n", client->addr, val); +} + + return sprintf(buf, "%d", val); +} + +/* + * I2C init/probing/exit functions + */ +static int as5712_54x_cpld_mux_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + int force, class; + struct i2c_mux_core *muxc; +#endif + + int chan=0; + struct as5712_54x_cpld_data *data; + int ret = -ENODEV; + const struct attribute_group *group = NULL; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) + goto exit; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + muxc = i2c_mux_alloc(adap, &client->dev, + chips[id->driver_data].nchans, + sizeof(*data), 0, + accton_i2c_cpld_mux_select_chan, + accton_i2c_cpld_mux_deselect_mux); + if (!muxc) + return -ENOMEM; + + data = i2c_mux_priv(muxc); + i2c_set_clientdata(client, data); + data->muxc = muxc; + data->client = client; +#else + + data = kzalloc(sizeof(struct as5712_54x_cpld_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto exit; + } + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); +#endif + data->type = id->driver_data; + if (data->type == as5712_54x_cpld2 || data->type == as5712_54x_cpld3) { + data->last_chan = chips[data->type].deselectChan; /* force the first selection */ + + /* Now create an adapter for each channel */ + for (chan = 0; chan < chips[data->type].nchans; chan++) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + force = 0; /* dynamic adap number */ + class = 0; /* no class by default */ + ret = i2c_mux_add_adapter(muxc, force, chan, class); + if (ret) +#else + data->virt_adaps[chan] = i2c_add_mux_adapter(adap, &client->dev, client, 0, chan, + 0, + as5712_54x_cpld_mux_select_chan, + as5712_54x_cpld_mux_deselect_mux); + if (data->virt_adaps[chan] == NULL) +#endif + { + ret = -ENODEV; + dev_err(&client->dev, "failed to register multiplexed adapter %d\n", chan); + goto exit_mux_register; + } + } + + dev_info(&client->dev, "registered %d multiplexed busses for I2C mux %s\n", + chan, client->name); + } + /* Register sysfs hooks */ + switch (data->type) + { + case as5712_54x_cpld1: + group = &as5712_54x_cpld1_group; + break; + case as5712_54x_cpld2: + group = &as5712_54x_cpld2_group; + break; + case as5712_54x_cpld3: + group = &as5712_54x_cpld3_group; + break; + default: + break; + } + + + if (group) { + ret = sysfs_create_group(&client->dev.kobj, group); + if (ret) { + goto exit_mux_register; + } + } + + as5712_54x_cpld_add_client(client); + return 0; + +exit_mux_register: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + i2c_mux_del_adapters(muxc); +#else + for (chan--; chan >= 0; chan--) { + i2c_del_mux_adapter(data->virt_adaps[chan]); + } +#endif + kfree(data); +exit: + return ret; +} + +static int as5712_54x_cpld_mux_remove(struct i2c_client *client) +{ + struct as5712_54x_cpld_data *data = i2c_get_clientdata(client); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + struct i2c_mux_core *muxc = data->muxc; + + i2c_mux_del_adapters(muxc); +#else + const struct chip_desc *chip = &chips[data->type]; + int chan; + const struct attribute_group *group = NULL; + + as5712_54x_cpld_remove_client(client); + + /* Remove sysfs hooks */ + switch (data->type) { + case as5712_54x_cpld1: + group = &as5712_54x_cpld1_group; + break; + case as5712_54x_cpld2: + group = &as5712_54x_cpld2_group; + break; + case as5712_54x_cpld3: + group = &as5712_54x_cpld3_group; + break; + default: + break; + } + + if (group) { + sysfs_remove_group(&client->dev.kobj, group); + } + + for (chan = 0; chan < chip->nchans; ++chan) { + if (data->virt_adaps[chan]) { + i2c_del_mux_adapter(data->virt_adaps[chan]); + data->virt_adaps[chan] = NULL; + } + } +#endif + kfree(data); + + return 0; +} + +static int as5712_54x_cpld_read_internal(struct i2c_client *client, u8 reg) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, reg); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static int as5712_54x_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_write_byte_data(client, reg, value); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +int as5712_54x_cpld_read(unsigned short cpld_addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = as5712_54x_cpld_read_internal(cpld_node->client, reg); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as5712_54x_cpld_read); + +int as5712_54x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = as5712_54x_cpld_write_internal(cpld_node->client, reg, value); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as5712_54x_cpld_write); + +#if 0 +int accton_i2c_cpld_mux_get_index(int adap_index) +{ + int i; + + for (i = 0; i < NUM_OF_ALL_CPLD_CHANS; i++) { + if (mux_adap_map[i] == adap_index) { + return i; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL(accton_i2c_cpld_mux_get_index); +#endif + +static struct i2c_driver as5712_54x_cpld_mux_driver = { + .driver = { + .name = "as5712_54x_cpld", + .owner = THIS_MODULE, + }, + .probe = as5712_54x_cpld_mux_probe, + .remove = as5712_54x_cpld_mux_remove, + .id_table = as5712_54x_cpld_mux_id, +}; + +static int __init as5712_54x_cpld_mux_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&as5712_54x_cpld_mux_driver); +} + +static void __exit as5712_54x_cpld_mux_exit(void) +{ + i2c_del_driver(&as5712_54x_cpld_mux_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("Accton I2C CPLD mux driver"); +MODULE_LICENSE("GPL"); + +module_init(as5712_54x_cpld_mux_init); +module_exit(as5712_54x_cpld_mux_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/leds-accton_as5712_54x.c b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/leds-accton_as5712_54x.c new file mode 100755 index 000000000000..cdea927368ae --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/leds-accton_as5712_54x.c @@ -0,0 +1,594 @@ +/* + * A LED driver for the accton_as5712_54x_led + * + * Copyright (C) 2013 Accton Technology Corporation. + * Brandon Chuang + * + * 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. + */ +#if 0 +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include + +extern int as5712_54x_cpld_read (unsigned short cpld_addr, u8 reg); +extern int as5712_54x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +extern void led_classdev_unregister(struct led_classdev *led_cdev); +extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev); +extern void led_classdev_resume(struct led_classdev *led_cdev); +extern void led_classdev_suspend(struct led_classdev *led_cdev); + +#define DRVNAME "as5712_54x_led" + +struct accton_as5712_54x_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[4]; /* Register value, 0 = LOC/DIAG/FAN LED + 1 = PSU1/PSU2 LED + 2 = FAN1-4 LED + 3 = FAN5-6 LED */ +}; + +static struct accton_as5712_54x_led_data *ledctl = NULL; + +/* LED related data + */ +#define LED_TYPE_PSU1_REG_MASK 0x03 +#define LED_MODE_PSU1_GREEN_MASK 0x02 +#define LED_MODE_PSU1_AMBER_MASK 0x01 +#define LED_MODE_PSU1_OFF_MASK 0x03 +#define LED_MODE_PSU1_AUTO_MASK 0x00 + +#define LED_TYPE_PSU2_REG_MASK 0x0C +#define LED_MODE_PSU2_GREEN_MASK 0x08 +#define LED_MODE_PSU2_AMBER_MASK 0x04 +#define LED_MODE_PSU2_OFF_MASK 0x0C +#define LED_MODE_PSU2_AUTO_MASK 0x00 + +#define LED_TYPE_DIAG_REG_MASK 0x0C +#define LED_MODE_DIAG_GREEN_MASK 0x08 +#define LED_MODE_DIAG_AMBER_MASK 0x04 +#define LED_MODE_DIAG_OFF_MASK 0x0C + +#define LED_TYPE_FAN_REG_MASK 0x03 +#define LED_MODE_FAN_GREEN_MASK 0x02 +#define LED_MODE_FAN_AMBER_MASK 0x01 +#define LED_MODE_FAN_OFF_MASK 0x03 +#define LED_MODE_FAN_AUTO_MASK 0x00 + +#define LED_TYPE_FAN1_REG_MASK 0x03 +#define LED_TYPE_FAN2_REG_MASK 0x0C +#define LED_TYPE_FAN3_REG_MASK 0x30 +#define LED_TYPE_FAN4_REG_MASK 0xC0 +#define LED_TYPE_FAN5_REG_MASK 0x03 +#define LED_TYPE_FAN6_REG_MASK 0x0C + +#define LED_MODE_FANX_GREEN_MASK 0x01 +#define LED_MODE_FANX_RED_MASK 0x02 +#define LED_MODE_FANX_OFF_MASK 0x00 + +#define LED_TYPE_LOC_REG_MASK 0x30 +#define LED_MODE_LOC_ON_MASK 0x00 +#define LED_MODE_LOC_OFF_MASK 0x10 +#define LED_MODE_LOC_BLINK_MASK 0x20 + +static const u8 led_reg[] = { + 0xA, /* LOC/DIAG/FAN LED*/ + 0xB, /* PSU1/PSU2 LED */ + 0x16, /* FAN1-4 LED */ + 0x17, /* FAN4-6 LED */ +}; + +enum led_type { + LED_TYPE_PSU1, + LED_TYPE_PSU2, + LED_TYPE_DIAG, + LED_TYPE_FAN, + LED_TYPE_FAN1, + LED_TYPE_FAN2, + LED_TYPE_FAN3, + LED_TYPE_FAN4, + LED_TYPE_FAN5, + LED_TYPE_LOC +}; + +enum led_light_mode { + LED_MODE_OFF = 0, + LED_MODE_GREEN, + LED_MODE_AMBER, + LED_MODE_RED, + LED_MODE_GREEN_BLINK, + LED_MODE_AMBER_BLINK, + LED_MODE_RED_BLINK, + LED_MODE_AUTO, +}; + +struct led_type_mode { + enum led_type type; + int type_mask; + enum led_light_mode mode; + int mode_mask; +}; + +static struct led_type_mode led_type_mode_data[] = { +{LED_TYPE_PSU1, LED_TYPE_PSU1_REG_MASK, LED_MODE_GREEN, LED_MODE_PSU1_GREEN_MASK}, +{LED_TYPE_PSU1, LED_TYPE_PSU1_REG_MASK, LED_MODE_AMBER, LED_MODE_PSU1_AMBER_MASK}, +{LED_TYPE_PSU1, LED_TYPE_PSU1_REG_MASK, LED_MODE_AUTO, LED_MODE_PSU1_AUTO_MASK}, +{LED_TYPE_PSU1, LED_TYPE_PSU1_REG_MASK, LED_MODE_OFF, LED_MODE_PSU1_OFF_MASK}, +{LED_TYPE_PSU2, LED_TYPE_PSU2_REG_MASK, LED_MODE_GREEN, LED_MODE_PSU2_GREEN_MASK}, +{LED_TYPE_PSU2, LED_TYPE_PSU2_REG_MASK, LED_MODE_AMBER, LED_MODE_PSU2_AMBER_MASK}, +{LED_TYPE_PSU2, LED_TYPE_PSU2_REG_MASK, LED_MODE_AUTO, LED_MODE_PSU2_AUTO_MASK}, +{LED_TYPE_PSU2, LED_TYPE_PSU2_REG_MASK, LED_MODE_OFF, LED_MODE_PSU2_OFF_MASK}, +{LED_TYPE_FAN, LED_TYPE_FAN_REG_MASK, LED_MODE_GREEN, LED_MODE_FAN_GREEN_MASK}, +{LED_TYPE_FAN, LED_TYPE_FAN_REG_MASK, LED_MODE_AMBER, LED_MODE_FAN_AMBER_MASK}, +{LED_TYPE_FAN, LED_TYPE_FAN_REG_MASK, LED_MODE_AUTO, LED_MODE_FAN_AUTO_MASK}, +{LED_TYPE_FAN, LED_TYPE_FAN_REG_MASK, LED_MODE_OFF, LED_MODE_FAN_OFF_MASK}, +{LED_TYPE_FAN1, LED_TYPE_FAN1_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 0}, +{LED_TYPE_FAN1, LED_TYPE_FAN1_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 0}, +{LED_TYPE_FAN1, LED_TYPE_FAN1_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 0}, +{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 2}, +{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 2}, +{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 2}, +{LED_TYPE_FAN3, LED_TYPE_FAN3_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 4}, +{LED_TYPE_FAN3, LED_TYPE_FAN3_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 4}, +{LED_TYPE_FAN3, LED_TYPE_FAN3_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 4}, +{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 6}, +{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 6}, +{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 6}, +{LED_TYPE_FAN5, LED_TYPE_FAN5_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 0}, +{LED_TYPE_FAN5, LED_TYPE_FAN5_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 0}, +{LED_TYPE_FAN5, LED_TYPE_FAN5_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 0}, +{LED_TYPE_DIAG, LED_TYPE_DIAG_REG_MASK, LED_MODE_GREEN, LED_MODE_DIAG_GREEN_MASK}, +{LED_TYPE_DIAG, LED_TYPE_DIAG_REG_MASK, LED_MODE_AMBER, LED_MODE_DIAG_AMBER_MASK}, +{LED_TYPE_DIAG, LED_TYPE_DIAG_REG_MASK, LED_MODE_OFF, LED_MODE_DIAG_OFF_MASK}, +{LED_TYPE_LOC, LED_TYPE_LOC_REG_MASK, LED_MODE_AMBER, LED_MODE_LOC_ON_MASK}, +{LED_TYPE_LOC, LED_TYPE_LOC_REG_MASK, LED_MODE_OFF, LED_MODE_LOC_OFF_MASK}, +{LED_TYPE_LOC, LED_TYPE_LOC_REG_MASK, LED_MODE_AMBER_BLINK, LED_MODE_LOC_BLINK_MASK} +}; + + +struct fanx_info_s { + u8 cname; /* device name */ + enum led_type type; + u8 reg_id; /* map to led_reg & reg_val */ +}; + +static struct fanx_info_s fanx_info[] = { + {'1', LED_TYPE_FAN1, 2}, + {'2', LED_TYPE_FAN2, 2}, + {'3', LED_TYPE_FAN3, 2}, + {'4', LED_TYPE_FAN4, 2}, + {'5', LED_TYPE_FAN5, 3} +}; + +static int led_reg_val_to_light_mode(enum led_type type, u8 reg_val) { + int i; + + for (i = 0; i < ARRAY_SIZE(led_type_mode_data); i++) { + + if (type != led_type_mode_data[i].type) + continue; + + if ((led_type_mode_data[i].type_mask & reg_val) == + led_type_mode_data[i].mode_mask) + { + return led_type_mode_data[i].mode; + } + } + + return 0; +} + +static u8 led_light_mode_to_reg_val(enum led_type type, + enum led_light_mode mode, u8 reg_val) { + int i; + + for (i = 0; i < ARRAY_SIZE(led_type_mode_data); i++) { + if (type != led_type_mode_data[i].type) + continue; + + if (mode != led_type_mode_data[i].mode) + continue; + + reg_val = led_type_mode_data[i].mode_mask | + (reg_val & (~led_type_mode_data[i].type_mask)); + } + + return reg_val; +} + +static int accton_as5712_54x_led_read_value(u8 reg) +{ + return as5712_54x_cpld_read(0x60, reg); +} + +static int accton_as5712_54x_led_write_value(u8 reg, u8 value) +{ + return as5712_54x_cpld_write(0x60, reg, value); +} + +static void accton_as5712_54x_led_update(void) +{ + mutex_lock(&ledctl->update_lock); + + if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) + || !ledctl->valid) { + int i; + + dev_dbg(&ledctl->pdev->dev, "Starting accton_as5712_54x_led update\n"); + + /* Update LED data + */ + for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { + int status = accton_as5712_54x_led_read_value(led_reg[i]); + + if (status < 0) { + ledctl->valid = 0; + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg[i], status); + goto exit; + } + else + { + ledctl->reg_val[i] = status; + } + } + + ledctl->last_updated = jiffies; + ledctl->valid = 1; + } + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void accton_as5712_54x_led_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode, + u8 reg, enum led_type type) +{ + int reg_val; + + mutex_lock(&ledctl->update_lock); + + reg_val = accton_as5712_54x_led_read_value(reg); + + if (reg_val < 0) { + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", reg, reg_val); + goto exit; + } + + reg_val = led_light_mode_to_reg_val(type, led_light_mode, reg_val); + accton_as5712_54x_led_write_value(reg, reg_val); + + /* to prevent the slow-update issue */ + ledctl->valid = 0; + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void accton_as5712_54x_led_psu_1_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as5712_54x_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU1); +} + +static enum led_brightness accton_as5712_54x_led_psu_1_get(struct led_classdev *cdev) +{ + accton_as5712_54x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_PSU1, ledctl->reg_val[1]); +} + +static void accton_as5712_54x_led_psu_2_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as5712_54x_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU2); +} + +static enum led_brightness accton_as5712_54x_led_psu_2_get(struct led_classdev *cdev) +{ + accton_as5712_54x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_PSU2, ledctl->reg_val[1]); +} + +static void accton_as5712_54x_led_fan_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as5712_54x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_FAN); +} + +static enum led_brightness accton_as5712_54x_led_fan_get(struct led_classdev *cdev) +{ + accton_as5712_54x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_FAN, ledctl->reg_val[0]); +} + + +static void accton_as5712_54x_led_fanx_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + enum led_type led_type1; + int reg_id; + int i, nsize; + int ncount = sizeof(fanx_info)/sizeof(struct fanx_info_s); + + for(i=0;iname); + + if (led_cdev->name[nsize-1] == fanx_info[i].cname) + { + led_type1 = fanx_info[i].type; + reg_id = fanx_info[i].reg_id; + accton_as5712_54x_led_set(led_cdev, led_light_mode, led_reg[reg_id], led_type1); + return; + } + } +} + + +static enum led_brightness accton_as5712_54x_led_fanx_get(struct led_classdev *cdev) +{ + enum led_type led_type1; + int reg_id; + int i, nsize; + int ncount = sizeof(fanx_info)/sizeof(struct fanx_info_s); + + for(i=0;iname); + + if (cdev->name[nsize-1] == fanx_info[i].cname) + { + led_type1 = fanx_info[i].type; + reg_id = fanx_info[i].reg_id; + accton_as5712_54x_led_update(); + return led_reg_val_to_light_mode(led_type1, ledctl->reg_val[reg_id]); + } + } + + + return led_reg_val_to_light_mode(LED_TYPE_FAN1, ledctl->reg_val[2]); +} + + +static void accton_as5712_54x_led_diag_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as5712_54x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_DIAG); +} + +static enum led_brightness accton_as5712_54x_led_diag_get(struct led_classdev *cdev) +{ + accton_as5712_54x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); +} + +static void accton_as5712_54x_led_loc_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as5712_54x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_LOC); +} + +static enum led_brightness accton_as5712_54x_led_loc_get(struct led_classdev *cdev) +{ + accton_as5712_54x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); +} + +static struct led_classdev accton_as5712_54x_leds[] = { + [LED_TYPE_PSU1] = { + .name = "accton_as5712_54x_led::psu1", + .default_trigger = "unused", + .brightness_set = accton_as5712_54x_led_psu_1_set, + .brightness_get = accton_as5712_54x_led_psu_1_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU2] = { + .name = "accton_as5712_54x_led::psu2", + .default_trigger = "unused", + .brightness_set = accton_as5712_54x_led_psu_2_set, + .brightness_get = accton_as5712_54x_led_psu_2_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN] = { + .name = "accton_as5712_54x_led::fan", + .default_trigger = "unused", + .brightness_set = accton_as5712_54x_led_fan_set, + .brightness_get = accton_as5712_54x_led_fan_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN1] = { + .name = "accton_as5712_54x_led::fan1", + .default_trigger = "unused", + .brightness_set = accton_as5712_54x_led_fanx_set, + .brightness_get = accton_as5712_54x_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN2] = { + .name = "accton_as5712_54x_led::fan2", + .default_trigger = "unused", + .brightness_set = accton_as5712_54x_led_fanx_set, + .brightness_get = accton_as5712_54x_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN3] = { + .name = "accton_as5712_54x_led::fan3", + .default_trigger = "unused", + .brightness_set = accton_as5712_54x_led_fanx_set, + .brightness_get = accton_as5712_54x_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN4] = { + .name = "accton_as5712_54x_led::fan4", + .default_trigger = "unused", + .brightness_set = accton_as5712_54x_led_fanx_set, + .brightness_get = accton_as5712_54x_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN5] = { + .name = "accton_as5712_54x_led::fan5", + .default_trigger = "unused", + .brightness_set = accton_as5712_54x_led_fanx_set, + .brightness_get = accton_as5712_54x_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_DIAG] = { + .name = "accton_as5712_54x_led::diag", + .default_trigger = "unused", + .brightness_set = accton_as5712_54x_led_diag_set, + .brightness_get = accton_as5712_54x_led_diag_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_LOC] = { + .name = "accton_as5712_54x_led::loc", + .default_trigger = "unused", + .brightness_set = accton_as5712_54x_led_loc_set, + .brightness_get = accton_as5712_54x_led_loc_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, +}; + +static int accton_as5712_54x_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as5712_54x_leds); i++) { + led_classdev_suspend(&accton_as5712_54x_leds[i]); + } + + return 0; +} + +static int accton_as5712_54x_led_resume(struct platform_device *dev) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as5712_54x_leds); i++) { + led_classdev_resume(&accton_as5712_54x_leds[i]); + } + + return 0; +} + +static int accton_as5712_54x_led_probe(struct platform_device *pdev) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(accton_as5712_54x_leds); i++) { + ret = led_classdev_register(&pdev->dev, &accton_as5712_54x_leds[i]); + + if (ret < 0) + break; + } + + /* Check if all LEDs were successfully registered */ + if (i != ARRAY_SIZE(accton_as5712_54x_leds)){ + int j; + + /* only unregister the LEDs that were successfully registered */ + for (j = 0; j < i; j++) { + led_classdev_unregister(&accton_as5712_54x_leds[i]); + } + } + + return ret; +} + +static int accton_as5712_54x_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(accton_as5712_54x_leds); i++) { + led_classdev_unregister(&accton_as5712_54x_leds[i]); + } + + return 0; +} + +static struct platform_driver accton_as5712_54x_led_driver = { + .probe = accton_as5712_54x_led_probe, + .remove = accton_as5712_54x_led_remove, + .suspend = accton_as5712_54x_led_suspend, + .resume = accton_as5712_54x_led_resume, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init accton_as5712_54x_led_init(void) +{ + int ret; + + ret = platform_driver_register(&accton_as5712_54x_led_driver); + if (ret < 0) { + goto exit; + } + + ledctl = kzalloc(sizeof(struct accton_as5712_54x_led_data), GFP_KERNEL); + if (!ledctl) { + ret = -ENOMEM; + platform_driver_unregister(&accton_as5712_54x_led_driver); + goto exit; + } + + mutex_init(&ledctl->update_lock); + + ledctl->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(ledctl->pdev)) { + ret = PTR_ERR(ledctl->pdev); + platform_driver_unregister(&accton_as5712_54x_led_driver); + kfree(ledctl); + goto exit; + } + +exit: + return ret; +} + +static void __exit accton_as5712_54x_led_exit(void) +{ + platform_device_unregister(ledctl->pdev); + platform_driver_unregister(&accton_as5712_54x_led_driver); + kfree(ledctl); +} + +module_init(accton_as5712_54x_led_init); +module_exit(accton_as5712_54x_led_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_as5712_54x_led driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/ym2651y.c b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/ym2651y.c new file mode 100755 index 000000000000..519530387e29 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/modules/ym2651y.c @@ -0,0 +1,683 @@ +/* + * An hwmon driver for the 3Y Power YM-2651Y Power Module + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * 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. + */ +#if 0 +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_FAN_DUTY_CYCLE 100 + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +enum chips { + YM2651, + YM2401, +}; + +/* Each client has this additional data + */ +struct ym2651y_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 chip; /* chip id */ + u8 capability; /* Register value */ + u16 status_word; /* Register value */ + u8 fan_fault; /* Register value */ + u8 over_temp; /* Register value */ + u16 v_out; /* Register value */ + u16 i_out; /* Register value */ + u16 p_out; /* Register value */ + u8 vout_mode; /* Register value */ + u16 temp; /* Register value */ + u16 fan_speed; /* Register value */ + u16 fan_duty_cycle[2]; /* Register value */ + u8 fan_dir[5]; /* Register value */ + u8 pmbus_revision; /* Register value */ + u8 mfr_id[10]; /* Register value */ + u8 mfr_model[16]; /* Register value */ + u8 mfr_revsion[3]; /* Register value */ + u16 mfr_vin_min; /* Register value */ + u16 mfr_vin_max; /* Register value */ + u16 mfr_iin_max; /* Register value */ + u16 mfr_iout_max; /* Register value */ + u16 mfr_pin_max; /* Register value */ + u16 mfr_pout_max; /* Register value */ + u16 mfr_vout_min; /* Register value */ + u16 mfr_vout_max; /* Register value */ +}; + +static ssize_t show_byte(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_word(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_linear(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_vout(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_over_temp(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_ascii(struct device *dev, struct device_attribute *da, + char *buf); +static struct ym2651y_data *ym2651y_update_device(struct device *dev); +static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value); + +enum ym2651y_sysfs_attributes { + PSU_POWER_ON = 0, + PSU_TEMP_FAULT, + PSU_POWER_GOOD, + PSU_FAN1_FAULT, + PSU_FAN_DIRECTION, + PSU_OVER_TEMP, + PSU_V_OUT, + PSU_I_OUT, + PSU_P_OUT, + PSU_TEMP1_INPUT, + PSU_FAN1_SPEED, + PSU_FAN1_DUTY_CYCLE, + PSU_PMBUS_REVISION, + PSU_MFR_ID, + PSU_MFR_MODEL, + PSU_MFR_REVISION, + PSU_MFR_VIN_MIN, + PSU_MFR_VIN_MAX, + PSU_MFR_VOUT_MIN, + PSU_MFR_VOUT_MAX, + PSU_MFR_IIN_MAX, + PSU_MFR_IOUT_MAX, + PSU_MFR_PIN_MAX, + PSU_MFR_POUT_MAX +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_power_on, S_IRUGO, show_word, NULL, PSU_POWER_ON); +static SENSOR_DEVICE_ATTR(psu_temp_fault, S_IRUGO, show_word, NULL, PSU_TEMP_FAULT); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_word, NULL, PSU_POWER_GOOD); +static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_fan_fault, NULL, PSU_FAN1_FAULT); +static SENSOR_DEVICE_ATTR(psu_over_temp, S_IRUGO, show_over_temp, NULL, PSU_OVER_TEMP); +static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, show_vout, NULL, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, show_linear, NULL, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, show_linear, NULL, PSU_P_OUT); +static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); +static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IWUSR | S_IRUGO, show_linear, set_fan_duty_cycle, PSU_FAN1_DUTY_CYCLE); +static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IRUGO, show_ascii, NULL, PSU_FAN_DIRECTION); +static SENSOR_DEVICE_ATTR(psu_pmbus_revision,S_IRUGO, show_byte, NULL, PSU_PMBUS_REVISION); +static SENSOR_DEVICE_ATTR(psu_mfr_id, S_IRUGO, show_ascii, NULL, PSU_MFR_ID); +static SENSOR_DEVICE_ATTR(psu_mfr_model, S_IRUGO, show_ascii, NULL, PSU_MFR_MODEL); +static SENSOR_DEVICE_ATTR(psu_mfr_revision, S_IRUGO, show_ascii, NULL, PSU_MFR_REVISION); +static SENSOR_DEVICE_ATTR(psu_mfr_vin_min, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MIN); +static SENSOR_DEVICE_ATTR(psu_mfr_vin_max, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_vout_min, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MIN); +static SENSOR_DEVICE_ATTR(psu_mfr_vout_max, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_iin_max, S_IRUGO, show_linear, NULL, PSU_MFR_IIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_iout_max, S_IRUGO, show_linear, NULL, PSU_MFR_IOUT_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_pin_max, S_IRUGO, show_linear, NULL, PSU_MFR_PIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_pout_max, S_IRUGO, show_linear, NULL, PSU_MFR_POUT_MAX); + +static struct attribute *ym2651y_attributes[] = { + &sensor_dev_attr_psu_power_on.dev_attr.attr, + &sensor_dev_attr_psu_temp_fault.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, + &sensor_dev_attr_psu_over_temp.dev_attr.attr, + &sensor_dev_attr_psu_v_out.dev_attr.attr, + &sensor_dev_attr_psu_i_out.dev_attr.attr, + &sensor_dev_attr_psu_p_out.dev_attr.attr, + &sensor_dev_attr_psu_temp1_input.dev_attr.attr, + &sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr, + &sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr, + &sensor_dev_attr_psu_fan_dir.dev_attr.attr, + &sensor_dev_attr_psu_pmbus_revision.dev_attr.attr, + &sensor_dev_attr_psu_mfr_id.dev_attr.attr, + &sensor_dev_attr_psu_mfr_model.dev_attr.attr, + &sensor_dev_attr_psu_mfr_revision.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vin_min.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_pout_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_iin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_pin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vout_min.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vout_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_iout_max.dev_attr.attr, + NULL +}; + +static ssize_t show_byte(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + + if (!data->valid) { + return 0; + } + + return (attr->index == PSU_PMBUS_REVISION) ? sprintf(buf, "%d\n", data->pmbus_revision) : + sprintf(buf, "0\n"); +} + +static ssize_t show_word(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + u16 status = 0; + + if (!data->valid) { + return 0; + } + + switch (attr->index) { + case PSU_POWER_ON: /* psu_power_on, low byte bit 6 of status_word, 0=>ON, 1=>OFF */ + status = (data->status_word & 0x40) ? 0 : 1; + break; + case PSU_TEMP_FAULT: /* psu_temp_fault, low byte bit 2 of status_word, 0=>Normal, 1=>temp fault */ + status = (data->status_word & 0x4) >> 2; + break; + case PSU_POWER_GOOD: /* psu_power_good, high byte bit 3 of status_word, 0=>OK, 1=>FAIL */ + status = (data->status_word & 0x800) ? 0 : 1; + break; + } + + return sprintf(buf, "%d\n", status); +} + +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(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct ym2651y_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; + + mutex_lock(&data->update_lock); + data->fan_duty_cycle[nr] = speed; + ym2651y_write_word(client, 0x3B + nr, data->fan_duty_cycle[nr]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_linear(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + + u16 value = 0; + int exponent, mantissa; + int multiplier = 1000; + + if (!data->valid) { + return 0; + } + + switch (attr->index) { + case PSU_V_OUT: + value = data->v_out; + break; + case PSU_I_OUT: + value = data->i_out; + break; + case PSU_P_OUT: + value = data->p_out; + break; + case PSU_TEMP1_INPUT: + value = data->temp; + break; + case PSU_FAN1_SPEED: + value = data->fan_speed; + multiplier = 1; + break; + case PSU_FAN1_DUTY_CYCLE: + value = data->fan_duty_cycle[0]; + multiplier = 1; + break; + case PSU_MFR_VIN_MIN: + value = data->mfr_vin_min; + break; + case PSU_MFR_VIN_MAX: + value = data->mfr_vin_max; + break; + case PSU_MFR_VOUT_MIN: + value = data->mfr_vout_min; + break; + case PSU_MFR_VOUT_MAX: + value = data->mfr_vout_max; + break; + case PSU_MFR_PIN_MAX: + value = data->mfr_pin_max; + break; + case PSU_MFR_POUT_MAX: + value = data->mfr_pout_max; + break; + case PSU_MFR_IOUT_MAX: + value = data->mfr_iout_max; + break; + case PSU_MFR_IIN_MAX: + value = data->mfr_iin_max; + 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 show_fan_fault(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + u8 shift; + + if (!data->valid) { + return 0; + } + + shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6; + + return sprintf(buf, "%d\n", data->fan_fault >> shift); +} + +static ssize_t show_over_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct ym2651y_data *data = ym2651y_update_device(dev); + + if (!data->valid) { + return 0; + } + + return sprintf(buf, "%d\n", data->over_temp >> 7); +} + +static ssize_t show_ascii(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + u8 *ptr = NULL; + + if (!data->valid) { + return 0; + } + + switch (attr->index) { + case PSU_FAN_DIRECTION: /* psu_fan_dir */ + ptr = data->fan_dir + 1; /* Skip the first byte since it is the length of string. */ + break; + case PSU_MFR_ID: /* psu_mfr_id */ + ptr = data->mfr_id + 1; /* The first byte is the count byte of string. */; + break; + case PSU_MFR_MODEL: /* psu_mfr_model */ + ptr = data->mfr_model + 1; /* The first byte is the count byte of string. */ + break; + case PSU_MFR_REVISION: /* psu_mfr_revision */ + ptr = data->mfr_revsion + 1; /* The first byte is the count byte of string. */ + break; + default: + return 0; + } + + return sprintf(buf, "%s\n", ptr); +} + +static ssize_t show_vout_by_mode(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct ym2651y_data *data = ym2651y_update_device(dev); + int exponent, mantissa; + int multiplier = 1000; + + if (!data->valid) { + return 0; + } + + exponent = two_complement_to_int(data->vout_mode, 5, 0x1f); + mantissa = data->v_out; + + return (exponent > 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) : + sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); +} + +static ssize_t show_vout(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ym2651y_data *data = i2c_get_clientdata(client); + + if (data->chip == YM2401) { + return show_vout_by_mode(dev, da, buf); + } + + return show_linear(dev, da, buf); +} + +static const struct attribute_group ym2651y_group = { + .attrs = ym2651y_attributes, +}; + +static int ym2651y_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct ym2651y_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct ym2651y_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->chip = dev_id->driver_data; + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &ym2651y_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &ym2651y_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int ym2651y_remove(struct i2c_client *client) +{ + struct ym2651y_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &ym2651y_group); + kfree(data); + + return 0; +} + +static const struct i2c_device_id ym2651y_id[] = { + { "ym2651", YM2651 }, + { "ym2401", YM2401 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ym2651y_id); + +static struct i2c_driver ym2651y_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ym2651", + }, + .probe = ym2651y_probe, + .remove = ym2651y_remove, + .id_table = ym2651y_id, + .address_list = normal_i2c, +}; + +static int ym2651y_read_byte(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int ym2651y_read_word(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_data(client, reg); +} + +static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_word_data(client, reg, value); +} + +static int ym2651y_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 ym2651y_data *ym2651y_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ym2651y_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int i, status, length; + u8 command, buf; + struct reg_data_byte regs_byte[] = { {0x19, &data->capability}, + {0x20, &data->vout_mode}, + {0x7d, &data->over_temp}, + {0x81, &data->fan_fault}, + {0x98, &data->pmbus_revision}}; + struct reg_data_word regs_word[] = { {0x79, &data->status_word}, + {0x8b, &data->v_out}, + {0x8c, &data->i_out}, + {0x96, &data->p_out}, + {0x8d, &data->temp}, + {0x3b, &(data->fan_duty_cycle[0])}, + {0x3c, &(data->fan_duty_cycle[1])}, + {0x90, &data->fan_speed}, + {0xa0, &data->mfr_vin_min}, + {0xa1, &data->mfr_vin_max}, + {0xa2, &data->mfr_iin_max}, + {0xa3, &data->mfr_pin_max}, + {0xa4, &data->mfr_vout_min}, + {0xa5, &data->mfr_vout_max}, + {0xa6, &data->mfr_iout_max}, + {0xa7, &data->mfr_pout_max}}; + + dev_dbg(&client->dev, "Starting ym2651 update\n"); + data->valid = 0; + + /* Read byte data */ + for (i = 0; i < ARRAY_SIZE(regs_byte); i++) { + status = ym2651y_read_byte(client, regs_byte[i].reg); + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", + regs_byte[i].reg, status); + goto exit; + } + else { + *(regs_byte[i].value) = status; + } + } + + /* Read word data */ + for (i = 0; i < ARRAY_SIZE(regs_word); i++) { + status = ym2651y_read_word(client, regs_word[i].reg); + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", + regs_word[i].reg, status); + goto exit; + } + else { + *(regs_word[i].value) = status; + } + } + + /* Read fan_direction */ + command = 0xC3; + status = ym2651y_read_block(client, command, data->fan_dir, + ARRAY_SIZE(data->fan_dir)-1); + data->fan_dir[ARRAY_SIZE(data->fan_dir)-1] = '\0'; + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + goto exit; + } + + /* Read mfr_id */ + command = 0x99; + status = ym2651y_read_block(client, command, data->mfr_id, + ARRAY_SIZE(data->mfr_id)-1); + data->mfr_id[ARRAY_SIZE(data->mfr_id)-1] = '\0'; + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + goto exit; + } + + /* Read mfr_model */ + command = 0x9a; + length = 1; + + /* Read first byte to determine the length of data */ + status = ym2651y_read_block(client, command, &buf, length); + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + goto exit; + } + + status = ym2651y_read_block(client, command, data->mfr_model, buf+1); + data->mfr_model[buf+1] = '\0'; + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + goto exit; + } + + /* Read mfr_revsion */ + command = 0x9b; + status = ym2651y_read_block(client, command, data->mfr_revsion, + ARRAY_SIZE(data->mfr_revsion)-1); + data->mfr_revsion[ARRAY_SIZE(data->mfr_revsion)-1] = '\0'; + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + goto exit; + } + + data->last_updated = jiffies; + data->valid = 1; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init ym2651y_init(void) +{ + return i2c_add_driver(&ym2651y_driver); +} + +static void __exit ym2651y_exit(void) +{ + i2c_del_driver(&ym2651y_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("3Y Power YM-2651Y driver"); +MODULE_LICENSE("GPL"); + +module_init(ym2651y_init); +module_exit(ym2651y_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/service/as5712-platform-monitor.service b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/service/as5712-platform-monitor.service new file mode 100755 index 000000000000..da5d283a6d61 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/service/as5712-platform-monitor.service @@ -0,0 +1,17 @@ +[Unit] +Description=Accton AS5712-54X Platform Monitoring service +Before=pmon.service +After=sysinit.target +DefaultDependencies=no + +[Service] +ExecStartPre=/usr/local/bin/accton_as5712_util.py install +ExecStart=/usr/local/bin/accton_as5712_monitor.py +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/setup.py b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/setup.py new file mode 100755 index 000000000000..f4cb5f960a4d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/setup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +import os +import sys +from setuptools import setup +os.listdir + +setup( + name='as5712_54x', + version='1.0', + description='Module to initialize Accton AS5712-54X platforms', + + packages=['as5712_54x'], + package_dir={'as5712_54x': 'as5712-54x/classes'}, +) + diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/utils/README b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/utils/README new file mode 100755 index 000000000000..44e03cab5f52 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/utils/README @@ -0,0 +1,117 @@ +Copyright (C) 2016 Accton Networks, Inc. + +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 3 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, see . + +Contents of this package: + patch - files under patch/ is for kernel and ONIE installer + for the kernel: + config-accton-as5712_54x.patch + for kernel configuration. + driver-i2c-muxes-pca954x-always-deselect.patch + for i2c_mux deselects after transaction. + driver-patches-for-accton-as5712-fan-psu-cpld.patch + for as5712's fan/psu/cpld/led/sfp drivers. + for ONIE: + onie_installer-accton-AS5712-54X.patch + for console port setting and copy util script o rootfs. + module - Contains source code of as5712 kernel driver modules. + +The late Sonic building scripts, pushed @Dec 5 2016, will automatically +create a docker container and run building process under it. +User is not necessary to handle docker environment creation. + +1. Download sonic-buildimage environment. + - Run "git clone https://github.com/Azure/sonic-buildimage". + - cd to sonic-buildimage and run "git submodule update --init --recursive". +2. Build kernel + - cd ./src/sonic-linux-kernel + - Copy patches and series from patch/kernel of this release to + sonic-linux-kernel/patch. + - Build kernel by "make". + - The built kernel package, linux-image-3.16.0-5-amd64_3.16.51-3+deb8u1_amd64.deb + , is generated. +3. Build installer + - Change directory back to sonic-buildimage/. + - Get onie_installer-accton-AS5712-54X.patch" from patch/installer. + - Change setting for AS5712-54X by patching build_image.sh. + "patch -p1 < onie_installer-accton-AS5712-54X.patch" + !!NOTICE, patching onie_installer-accton-AS5712-54X.patch comments out the + "git status" checking at build_image.sh. + - The account and password of installed OS can be given at rules/config. + The default user and password are "admin" & "YourPaSsWoRd" respectively. + - Run "make configure PLATFORM=broadcom" + - Copy the built kernel debian package to target/debs/. + The file is linux-image-3.16.0-5-amd64_*_amd64.deb under directory + src/sonic-linux-kernel/. + - Run "make target/sonic-generic.bin" + - Get the installer, target/sonic-generic.bin, to target machine and install. + +All Linux kernel code is licensed under the GPLv1. All other code is +licensed under the GPLv3. Please see the LICENSE file for copies of +both licenses. + +The code for integacting with Accton AS5712-54X has 2 parts, +kernel drivers and operational script. +The kernel drivers of peripherals are under module/ directory. +1. These drivers are patched into kernel by + driver-patches-for-accton-as5712-fan-psu-cpld.patch + Or you can build the driver under module/ by setting environment variable, + KERNEL_SRC, to proper linux built directory and run make. + It may be sonic-linux-kernel/linux-3.*/debian/build/build_amd64_none_amd64/. +2. A operational script, accton_as5712_util.py, for device initializatian and + peripheral accessing should be installed at /usr/bin. + This script is generated by onie_installer-accton-AS5712-54X.patch. + It's done by patching onie_installer-accton-AS5712-54X.patch at build-image. + Run "accton_as5712_util.py install" to install drivers. + +To initialize the system, run "accton_as5712_util.py install". +To clean up the drivers & devices, run "accton_as5712_util.py clean". +To dump information of sensors, run "accton_as5712_util.py show". +To dump SFP EEPROM, run "accton_as5712_util.py sff". +To set fan speed, run "accton_as5712_util.py set fan". +To enable/disable SFP emission, run "accton_as5712_util.py set sfp". +To set system LEDs' color, run "accton_as5712_util.py set led" +For more information, run "accton_as5712_util.py --help". + +==================================================================== +Besides applying accton_as5712_util.py to access peripherals, you can +access peripherals by sysfs nodes directly after the installation is run. + +System LED: + There are 5 system LEDs at the lower-left corner of front panel. + They are loc, diag, fan, ps1, and ps2. + The sysfs interface color mappings are as follows: + Brightness: + 0 => off + 1 => green + 2 => amber + 3 => red + 4 => blue + But not all colors are available for each LED. + +Fan Control: + There are 10 fans inside 5 fan modules. + All fans share 1 duty setting, ranged from 0~100. + +Thermal sensers: + 3 temperature sensors are controlled by the lm75 kernel modules. + +PSUs: + There 2 power supplies slot at the left/right side of the back. + Once if a PSU is not plugged, the status of it is shown failed. + +There are 48 SFP+ and 6 QSFP modules are equipped. +Before operating on PSU and QSFP+, please make sure it is well plugged. +Otherwise, operation is going to fail. + diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/utils/accton_as5712_monitor.py b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/utils/accton_as5712_monitor.py new file mode 100755 index 000000000000..84ab2d83bab4 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/utils/accton_as5712_monitor.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# +# ------------------------------------------------------------------ + +try: + import os + import sys, getopt + import subprocess + import click + import imp + import logging + import logging.config + import types + import time # this is only being used as part of the example + import traceback + from tabulate import tabulate + from as5712_54x.fanutil import FanUtil + from as5712_54x.thermalutil import ThermalUtil +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + +# Deafults +VERSION = '1.0' +FUNCTION_NAME = 'accton_as5712_monitor' + +global log_file +global log_level + +# Make a class we can use to capture stdout and sterr in the log +class accton_as5712_monitor(object): + # static temp var + _ori_temp = 0 + _new_perc = 0 + _ori_perc = 0 + + def __init__(self, log_file, log_level): + """Needs a logger and a logger level.""" + # set up logging to file + logging.basicConfig( + filename=log_file, + filemode='w', + level=log_level, + format= '[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s', + datefmt='%H:%M:%S' + ) + + # set up logging to console + if log_level == logging.DEBUG: + console = logging.StreamHandler() + console.setLevel(log_level) + formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + + logging.debug('SET. logfile:%s / loglevel:%d', log_file, log_level) + + def manage_fans(self): + FAN_LEV1_UP_TEMP = 57500 # temperature + FAN_LEV1_DOWN_TEMP = 0 # unused + FAN_LEV1_SPEED_PERC = 100 # percentage*/ + + FAN_LEV2_UP_TEMP = 53000 + FAN_LEV2_DOWN_TEMP = 52700 + FAN_LEV2_SPEED_PERC = 80 + + FAN_LEV3_UP_TEMP = 49500 + FAN_LEV3_DOWN_TEMP = 47700 + FAN_LEV3_SPEED_PERC = 65 + + FAN_LEV4_UP_TEMP = 0 # unused + FAN_LEV4_DOWN_TEMP = 42700 + FAN_LEV4_SPEED_PERC = 40 + + + thermal = ThermalUtil() + fan = FanUtil() + + temp1 = thermal.get_thermal_1_val() + if temp1 is None: + return False + + temp2 = thermal.get_thermal_2_val() + if temp2 is None: + return False + + new_temp = (temp1 + temp2) / 2 + + for x in range(fan.get_idx_fan_start(), fan.get_num_fans()+1): + fan_stat = fan.get_fan_status(x) + if fan_stat is None: + return False + if fan_stat is False: + self._new_perc = FAN_LEV1_SPEED_PERC + logging.debug('INFO. SET new_perc to %d (FAN fault. fan_num:%d)', self._new_perc, x) + break + logging.debug('INFO. fan_stat is True (fan_num:%d)', x) + + if fan_stat is not None and fan_stat is not False: + diff = new_temp - self._ori_temp + if diff == 0: + logging.debug('INFO. RETURN. THERMAL temp not changed. %d / %d (new_temp / ori_temp)', new_temp, self._ori_temp) + return True + else: + if diff >= 0: + is_up = True + logging.debug('INFO. THERMAL temp UP %d / %d (new_temp / ori_temp)', new_temp, self._ori_temp) + else: + is_up = False + logging.debug('INFO. THERMAL temp DOWN %d / %d (new_temp / ori_temp)', new_temp, self._ori_temp) + + if is_up is True: + if new_temp >= FAN_LEV1_UP_TEMP: + self._new_perc = FAN_LEV1_SPEED_PERC + elif new_temp >= FAN_LEV2_UP_TEMP: + self._new_perc = FAN_LEV2_SPEED_PERC + elif new_temp >= FAN_LEV3_UP_TEMP: + self._new_perc = FAN_LEV3_SPEED_PERC + else: + self._new_perc = FAN_LEV4_SPEED_PERC + logging.debug('INFO. SET. FAN_SPEED as %d (new THERMAL temp:%d)', self._new_perc, new_temp) + else: + if new_temp <= FAN_LEV4_DOWN_TEMP: + self._new_perc = FAN_LEV4_SPEED_PERC + elif new_temp <= FAN_LEV3_DOWN_TEMP: + self._new_perc = FAN_LEV3_SPEED_PERC + elif new_temp <= FAN_LEV2_DOWN_TEMP: + self._new_perc = FAN_LEV2_SPEED_PERC + else: + self._new_perc = FAN_LEV1_SPEED_PERC + logging.debug('INFO. SET. FAN_SPEED as %d (new THERMAL temp:%d)', self._new_perc, new_temp) + + if self._ori_perc == self._new_perc: + logging.debug('INFO. RETURN. FAN speed not changed. %d / %d (new_perc / ori_perc)', self._new_perc, self._ori_perc) + return True + + set_stat = fan.set_fan_duty_cycle(fan.get_idx_fan_start(), self._new_perc) + if set_stat is True: + logging.debug('INFO: PASS. set_fan_duty_cycle (%d)', self._new_perc) + else: + logging.debug('INFO: FAIL. set_fan_duty_cycle (%d)', self._new_perc) + + logging.debug('INFO: GET. ori_perc is %d. ori_temp is %d', self._ori_perc, self._ori_temp) + self._ori_perc = self._new_perc + self._ori_temp = new_temp + logging.debug('INFO: UPDATE. ori_perc to %d. ori_temp to %d', self._ori_perc, self._ori_temp) + + return True + +def main(argv): + log_file = '%s.log' % FUNCTION_NAME + log_level = logging.INFO + if len(sys.argv) != 1: + try: + opts, args = getopt.getopt(argv,'hdl:',['lfile=']) + except getopt.GetoptError: + print 'Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + for opt, arg in opts: + if opt == '-h': + print 'Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + elif opt in ('-d', '--debug'): + log_level = logging.DEBUG + elif opt in ('-l', '--lfile'): + log_file = arg + + monitor = accton_as5712_monitor(log_file, log_level) + + # Loop forever, doing something useful hopefully: + while True: + monitor.manage_fans() + time.sleep(1) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/platform/broadcom/sonic-platform-modules-accton/as5712-54x/utils/accton_as5712_util.py b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/utils/accton_as5712_util.py new file mode 100755 index 000000000000..e958cb596862 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5712-54x/utils/accton_as5712_util.py @@ -0,0 +1,602 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 Accton Networks, Inc. +# +# 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 3 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, see . + +""" +Usage: %(scriptName)s [options] command object + +options: + -h | --help : this help message + -d | --debug : run with debug mode + -f | --force : ignore error during installation or clean +command: + install : install drivers and generate related sysfs nodes + clean : uninstall drivers and remove related sysfs nodes + show : show all systen status + sff : dump SFP eeprom + set : change board setting with fan|led|sfp +""" + +import os +import commands +import sys, getopt +import logging +import re +import time +from collections import namedtuple + +PROJECT_NAME = 'as5712_54x' +version = '0.2.0' +verbose = False +DEBUG = False +args = [] +ALL_DEVICE = {} +DEVICE_NO = {'led':5, 'fan1':1, 'fan2':1,'fan3':1,'fan4':1,'fan5':1,'thermal':3, 'psu':2, 'sfp':54} + + +led_prefix ='/sys/devices/platform/as5712_54x_led/leds/accton_'+PROJECT_NAME+'_led::' +fan_prefix ='/sys/devices/platform/as5712_54x_' +hwmon_types = {'led': ['diag','fan','loc','psu1','psu2'], + 'fan1': ['fan'], + 'fan2': ['fan'], + 'fan3': ['fan'], + 'fan4': ['fan'], + 'fan5': ['fan'], + } +hwmon_nodes = {'led': ['brightness'] , + 'fan1': ['fan1_duty_cycle_percentage', 'fan1_fault', 'fan1_speed_rpm', 'fan1_direction', 'fanr1_fault', 'fanr1_speed_rpm'], + 'fan2': ['fan2_duty_cycle_percentage','fan2_fault', 'fan2_speed_rpm', 'fan2_direction', 'fanr2_fault', 'fanr2_speed_rpm'], + 'fan3': ['fan3_duty_cycle_percentage','fan3_fault', 'fan3_speed_rpm', 'fan3_direction', 'fanr3_fault', 'fanr3_speed_rpm'], + 'fan4': ['fan4_duty_cycle_percentage','fan4_fault', 'fan4_speed_rpm', 'fan4_direction', 'fanr4_fault', 'fanr4_speed_rpm'], + 'fan5': ['fan5_duty_cycle_percentage','fan5_fault', 'fan5_speed_rpm', 'fan5_direction', 'fanr5_fault', 'fanr5_speed_rpm'], + } +hwmon_prefix ={'led': led_prefix, + 'fan1': fan_prefix, + 'fan2': fan_prefix, + 'fan3': fan_prefix, + 'fan4': fan_prefix, + 'fan5': fan_prefix, + } + +i2c_prefix = '/sys/bus/i2c/devices/' +i2c_bus = {'thermal': ['61-0048','62-0049', '63-004a'] , + 'psu': ['57-0050','58-0053'], + 'sfp': ['-0050']} +i2c_nodes = { + 'thermal': ['hwmon/hwmon*/temp1_input'] , + 'psu': ['psu_present ', 'psu_power_good'] , + 'sfp': ['sfp_is_present ', 'sfp_tx_disable']} + +sfp_map = [2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 52, 54, 51, 53, 55] +mknod =[ +'echo as5712_54x_cpld1 0x60 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo as5712_54x_cpld2 0x61 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo as5712_54x_cpld3 0x62 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-1/new_device', + +# PSU-1 +'echo as5712_54x_psu1 0x38 > /sys/bus/i2c/devices/i2c-57/new_device', +'echo cpr_4011_4mxx 0x3c > /sys/bus/i2c/devices/i2c-57/new_device', +'echo as5712_54x_psu1 0x50 > /sys/bus/i2c/devices/i2c-57/new_device', + +# PSU-2 +'echo as5712_54x_psu2 0x3b > /sys/bus/i2c/devices/i2c-58/new_device', +'echo cpr_4011_4mxx 0x3f > /sys/bus/i2c/devices/i2c-58/new_device', +'echo as5712_54x_psu2 0x53 > /sys/bus/i2c/devices/i2c-58/new_device', + +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-61/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-62/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-63/new_device', + +#EERPOM +'echo 24c02 0x57 > /sys/bus/i2c/devices/i2c-1/new_device', +] + +mknod2 =[ +'echo as5712_54x_cpld1 0x60 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo as5712_54x_cpld2 0x61 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo as5712_54x_cpld3 0x62 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-0/new_device', + +# PSU-1 +'echo as5712_54x_psu1 0x38 > /sys/bus/i2c/devices/i2c-57/new_device', +'echo cpr_4011_4mxx 0x3c > /sys/bus/i2c/devices/i2c-57/new_device', +'echo as5712_54x_psu1 0x50 > /sys/bus/i2c/devices/i2c-57/new_device', + +# PSU-2 +'echo as5712_54x_psu2 0x3b > /sys/bus/i2c/devices/i2c-58/new_device', +'echo cpr_4011_4mxx 0x3f > /sys/bus/i2c/devices/i2c-58/new_device', +'echo as5712_54x_psu2 0x53 > /sys/bus/i2c/devices/i2c-58/new_device', + +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-61/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-62/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-63/new_device', + +#EERPOM +'echo 24c02 0x57 > /sys/bus/i2c/devices/i2c-1/new_device', +] + +FORCE = 0 +logging.basicConfig(filename= PROJECT_NAME+'.log', filemode='w',level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) + + +if DEBUG == True: + print sys.argv[0] + print 'ARGV :', sys.argv[1:] + + +def main(): + global DEBUG + global args + global FORCE + + if len(sys.argv)<2: + show_help() + + options, args = getopt.getopt(sys.argv[1:], 'hdf', ['help', + 'debug', + 'force', + ]) + if DEBUG == True: + print options + print args + print len(sys.argv) + + for opt, arg in options: + if opt in ('-h', '--help'): + show_help() + elif opt in ('-d', '--debug'): + DEBUG = True + logging.basicConfig(level=logging.INFO) + elif opt in ('-f', '--force'): + FORCE = 1 + else: + logging.info('no option') + for arg in args: + if arg == 'install': + do_install() + elif arg == 'clean': + do_uninstall() + elif arg == 'show': + device_traversal() + elif arg == 'sff': + if len(args)!=2: + show_eeprom_help() + elif int(args[1]) ==0 or int(args[1]) > DEVICE_NO['sfp']: + show_eeprom_help() + else: + show_eeprom(args[1]) + return + elif arg == 'set': + if len(args)<3: + show_set_help() + else: + set_device(args[1:]) + return + else: + show_help() + + + return 0 + +def show_help(): + print __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]} + sys.exit(0) + +def show_set_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print cmd +" [led|sfp|fan]" + print " use \""+ cmd + " led 0-4 \" to set led color" + print " use \""+ cmd + " fan 0-100\" to set fan duty percetage" + print " use \""+ cmd + " sfp 1-54 {0|1}\" to set sfp# tx_disable" + sys.exit(0) + +def show_eeprom_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print " use \""+ cmd + " 1-54 \" to dump sfp# eeprom" + sys.exit(0) + +def my_log(txt): + if DEBUG == True: + print "[ACCTON DBG]: "+txt + return + +def log_os_system(cmd, show): + logging.info('Run :'+cmd) + status = 1 + output = "" + status, output = commands.getstatusoutput(cmd) + my_log (cmd +"with result:" + str(status)) + my_log ("cmd:" + cmd) + my_log (" output:"+output) + if status: + logging.info('Failed :'+cmd) + if show: + print('Failed :'+cmd) + return status, output + +def driver_inserted(): + ret, lsmod = log_os_system("lsmod| grep accton", 0) + logging.info('mods:'+lsmod) + if len(lsmod) ==0: + return False + + + +kos = [ +'depmod -ae', +'modprobe i2c_dev', +'modprobe i2c_mux_pca954x', +'modprobe optoe', +'modprobe i2c-mux-accton_as5712_54x_cpld', +'modprobe cpr_4011_4mxx', +'modprobe ym2651y', +'modprobe accton_as5712_54x_fan', +'modprobe leds-accton_as5712_54x', +'modprobe accton_as5712_54x_psu'] + +def driver_install(): + global FORCE + for i in range(0,len(kos)): + status, output = log_os_system(kos[i], 1) + if status: + if FORCE == 0: + return status + return 0 + +def driver_uninstall(): + global FORCE + for i in range(0,len(kos)): + rm = kos[-(i+1)].replace("modprobe", "modprobe -rq") + rm = rm.replace("insmod", "rmmod") + status, output = log_os_system(rm, 1) + if status: + if FORCE == 0: + return status + return 0 + + + +def i2c_order_check(): + # i2c bus 0 and 1 might be installed in different order. + # Here check if 0x70 is exist @ i2c-0 + tmp = "echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-1/new_device" + status, output = log_os_system(tmp, 0) + if not device_exist(): + order = 1 + else: + order = 0 + tmp = "echo 0x70 > /sys/bus/i2c/devices/i2c-1/delete_device" + status, output = log_os_system(tmp, 0) + return order + +def device_install(): + global FORCE + + order = i2c_order_check() + # if 0x76 is not exist @i2c-0, use reversed bus order + if order: + for i in range(0,len(mknod2)): + #for pca954x need times to built new i2c buses + if mknod2[i].find('pca954') != -1: + time.sleep(2) + + status, output = log_os_system(mknod2[i], 1) + if status: + print output + if FORCE == 0: + return status + else: + for i in range(0,len(mknod)): + #for pca954x need times to built new i2c buses + if mknod[i].find('pca954') != -1: + time.sleep(2) + + status, output = log_os_system(mknod[i], 1) + if status: + print output + if FORCE == 0: + return status + + for i in range(0,len(sfp_map)): + status, output =log_os_system("echo optoe1 0x50 > /sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/new_device", 1) + if status: + print output + if FORCE == 0: + return status + status, output =log_os_system("echo port"+str(i)+" > /sys/bus/i2c/devices/"+str(sfp_map[i])+"-0050/port_name", 1) + if status: + print output + if FORCE == 0: + return status + + return + +def device_uninstall(): + global FORCE + + status, output =log_os_system("ls /sys/bus/i2c/devices/0-0070", 0) + if status==0: + I2C_ORDER=1 + else: + I2C_ORDER=0 + + for i in range(0,len(sfp_map)): + target = "/sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/delete_device" + status, output =log_os_system("echo 0x50 > "+ target, 1) + if status: + print output + if FORCE == 0: + return status + + if I2C_ORDER==0: + nodelist = mknod + else: + nodelist = mknod2 + + for i in range(len(nodelist)): + target = nodelist[-(i+1)] + temp = target.split() + del temp[1] + temp[-1] = temp[-1].replace('new_device', 'delete_device') + status, output = log_os_system(" ".join(temp), 1) + if status: + print output + if FORCE == 0: + return status + + return + +def system_ready(): + if driver_inserted() == False: + return False + if not device_exist(): + return False + return True + +def do_install(): + print "Checking system...." + if driver_inserted() == False: + print "No driver, installing...." + status = driver_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" drivers detected...." + if not device_exist(): + print "No device, installing...." + status = device_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" devices detected...." + return + +def do_uninstall(): + print "Checking system...." + if not device_exist(): + print PROJECT_NAME.upper() +" has no device installed...." + else: + print "Removing device...." + status = device_uninstall() + if status: + if FORCE == 0: + return status + + if driver_inserted()== False : + print PROJECT_NAME.upper() +" has no driver installed...." + else: + print "Removing installed driver...." + status = driver_uninstall() + if status: + if FORCE == 0: + return status + + return + +def devices_info(): + global DEVICE_NO + global ALL_DEVICE + global i2c_bus, hwmon_types + for key in DEVICE_NO: + ALL_DEVICE[key]= {} + for i in range(0,DEVICE_NO[key]): + ALL_DEVICE[key][key+str(i+1)] = [] + + for key in i2c_bus: + buses = i2c_bus[key] + nodes = i2c_nodes[key] + for i in range(0,len(buses)): + for j in range(0,len(nodes)): + if 'fan' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ buses[i]+"/fan"+str(k+1)+"_"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + elif 'sfp' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ str(sfp_map[k])+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + else: + node = key+str(i+1) + path = i2c_prefix+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + + for key in hwmon_types: + itypes = hwmon_types[key] + nodes = hwmon_nodes[key] + for i in range(0,len(itypes)): + for j in range(0,len(nodes)): + node = key+"_"+itypes[i] + path = hwmon_prefix[key]+ itypes[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][ key+str(i+1)].append(path) + + #show dict all in the order + if DEBUG == True: + for i in sorted(ALL_DEVICE.keys()): + print(i+": ") + for j in sorted(ALL_DEVICE[i].keys()): + print(" "+j) + for k in (ALL_DEVICE[i][j]): + print(" "+" "+k) + return + +def show_eeprom(index): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + node = ALL_DEVICE['sfp'] ['sfp'+str(index)][0] + node = node.replace(node.split("/")[-1], 'sfp_eeprom') + # check if got hexdump command in current environment + ret, log = log_os_system("which hexdump", 0) + ret, log2 = log_os_system("which busybox hexdump", 0) + if len(log): + hex_cmd = 'hexdump' + elif len(log2): + hex_cmd = ' busybox hexdump' + else: + log = 'Failed : no hexdump cmd!!' + logging.info(log) + print log + return 1 + + print node + ":" + ret, log = log_os_system("cat "+node+"| "+hex_cmd+" -C", 1) + if ret==0: + print log + else: + print "**********device no found**********" + return + +def set_device(args): + global DEVICE_NO + global ALL_DEVICE + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + + if args[0]=='led': + if int(args[1])>4: + show_set_help() + return + #print ALL_DEVICE['led'] + for i in range(0,len(ALL_DEVICE['led'])): + for k in (ALL_DEVICE['led']['led'+str(i+1)]): + ret, log = log_os_system("echo "+args[1]+" >"+k, 1) + if ret: + return ret + elif args[0]=='fan': + if int(args[1])>100: + show_set_help() + return + #print ALL_DEVICE['fan'] + #fan1~6 is all fine, all fan share same setting + node = ALL_DEVICE['fan1'] ['fan11'][0] + node = node.replace(node.split("/")[-1], 'fan1_duty_cycle_percentage') + ret, log = log_os_system("cat "+ node, 1) + if ret==0: + print ("Previous fan duty: " + log.strip() +"%") + ret, log = log_os_system("echo "+args[1]+" >"+node, 1) + if ret==0: + print ("Current fan duty: " + args[1] +"%") + return ret + elif args[0]=='sfp': + if int(args[1])> DEVICE_NO[args[0]] or int(args[1])==0: + show_set_help() + return + if len(args)<2: + show_set_help() + return + + if int(args[2])>1: + show_set_help() + return + + #print ALL_DEVICE[args[0]] + for i in range(0,len(ALL_DEVICE[args[0]])): + for j in ALL_DEVICE[args[0]][args[0]+str(args[1])]: + if j.find('tx_disable')!= -1: + ret, log = log_os_system("echo "+args[2]+" >"+ j, 1) + if ret: + return ret + + return + +#get digits inside a string. +#Ex: 31 for "sfp31" +def get_value(input): + digit = re.findall('\d+', input) + return int(digit[0]) + +def device_traversal(): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + for i in sorted(ALL_DEVICE.keys()): + print("============================================") + print(i.upper()+": ") + print("============================================") + + for j in sorted(ALL_DEVICE[i].keys(), key=get_value): + print " "+j+":", + for k in (ALL_DEVICE[i][j]): + ret, log = log_os_system("cat "+k, 0) + func = k.split("/")[-1].strip() + func = re.sub(j+'_','',func,1) + func = re.sub(i.lower()+'_','',func,1) + if ret==0: + print func+"="+log+" ", + else: + print func+"="+"X"+" ", + print + print("----------------------------------------------------------------") + + + print + return + +def device_exist(): + ret1, log = log_os_system("ls "+i2c_prefix+"*0070", 0) + ret2, log = log_os_system("ls "+i2c_prefix+"i2c-2", 0) + return not(ret1 or ret2) + +if __name__ == "__main__": + main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/Makefile b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/Makefile new file mode 100644 index 000000000000..697dcff656c3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/Makefile @@ -0,0 +1,4 @@ +obj-m:=accton_i2c_cpld.o x86-64-accton-as5812-54t-fan.o \ + x86-64-accton-as5812-54t-leds.o x86-64-accton-as5812-54t-psu.o \ + x86-64-accton-as5812-54t-sfp.o ym2651y.o + diff --git a/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/accton_i2c_cpld.c b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/accton_i2c_cpld.c new file mode 100644 index 000000000000..c01d6bcca228 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/accton_i2c_cpld.c @@ -0,0 +1,330 @@ +/* + * A hwmon driver for the accton_i2c_cpld + * + * Copyright (C) 2013 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +/* Addresses scanned for accton_i2c_cpld + */ +static const unsigned short normal_i2c[] = { 0x31, 0x35, 0x60, 0x61, 0x62, 0x64, I2C_CLIENT_END }; + +static ssize_t show_cpld_version(struct device *dev, struct device_attribute *attr, char *buf) +{ + int val = 0; + struct i2c_client *client = to_i2c_client(dev); + + val = i2c_smbus_read_byte_data(client, 0x1); + + if (val < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x1) err %d\n", client->addr, val); + } + + return sprintf(buf, "%d", val); +} + +static struct device_attribute ver = __ATTR(version, 0600, show_cpld_version, NULL); + +static void accton_i2c_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void accton_i2c_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + + mutex_unlock(&list_lock); +} + +static int accton_i2c_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_dbg(&client->dev, "i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + status = sysfs_create_file(&client->dev.kobj, &ver.attr); + if (status) { + goto exit; + } + + dev_info(&client->dev, "chip found\n"); + accton_i2c_cpld_add_client(client); + + return 0; + +exit: + return status; +} + +static int accton_i2c_cpld_remove(struct i2c_client *client) +{ + sysfs_remove_file(&client->dev.kobj, &ver.attr); + accton_i2c_cpld_remove_client(client); + + return 0; +} + +static const struct i2c_device_id accton_i2c_cpld_id[] = { + { "accton_i2c_cpld", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, accton_i2c_cpld_id); + +static struct i2c_driver accton_i2c_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "accton_i2c_cpld", + }, + .probe = accton_i2c_cpld_probe, + .remove = accton_i2c_cpld_remove, + .id_table = accton_i2c_cpld_id, + .address_list = normal_i2c, +}; + +int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = i2c_smbus_read_byte_data(cpld_node->client, reg); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(accton_i2c_cpld_read); + +int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = i2c_smbus_write_byte_data(cpld_node->client, reg, value); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(accton_i2c_cpld_write); + +static int __init accton_i2c_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&accton_i2c_cpld_driver); +} + +static void __exit accton_i2c_cpld_exit(void) +{ + i2c_del_driver(&accton_i2c_cpld_driver); +} + +static struct dmi_system_id as7512_dmi_table[] = { + { + .ident = "Accton AS7512", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS7512"), + }, + }, + { + .ident = "Accton AS7512", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS7512"), + }, + }, +}; + +int platform_accton_as7512_32x(void) +{ + return dmi_check_system(as7512_dmi_table); +} +EXPORT_SYMBOL(platform_accton_as7512_32x); + +static struct dmi_system_id as7712_dmi_table[] = { + { + .ident = "Accton AS7712", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS7712"), + }, + }, + { + .ident = "Accton AS7712", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS7712"), + }, + }, +}; + +int platform_accton_as7712_32x(void) +{ + return dmi_check_system(as7712_dmi_table); +} +EXPORT_SYMBOL(platform_accton_as7712_32x); + +static struct dmi_system_id as5812_54t_dmi_table[] = { + { + .ident = "Accton AS5812 54t", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS5812-54T"), + }, + }, + { + .ident = "Accton AS5812 54t", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS5812-54T"), + }, + }, +}; + +int platform_accton_as5812_54t(void) +{ + return dmi_check_system(as5812_54t_dmi_table); +} +EXPORT_SYMBOL(platform_accton_as5812_54t); + +static struct dmi_system_id as5512_54x_dmi_table[] = { + { + .ident = "Accton AS5512", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS5512"), + }, + }, + { + .ident = "Accton AS5512", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS5512"), + }, + }, +}; + +int platform_accton_as5512_54x(void) +{ + return dmi_check_system(as5512_54x_dmi_table); +} +EXPORT_SYMBOL(platform_accton_as5512_54x); + +static struct dmi_system_id as7716_dmi_table[] = { + { + .ident = "Accton AS7716", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS7716"), + }, + }, + { + .ident = "Accton AS7716", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS7716"), + }, + }, +}; + +int platform_accton_as7716_32x(void) +{ + return dmi_check_system(as7716_dmi_table); +} +EXPORT_SYMBOL(platform_accton_as7716_32x); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_i2c_cpld driver"); +MODULE_LICENSE("GPL"); + +module_init(accton_i2c_cpld_init); +module_exit(accton_i2c_cpld_exit); diff --git a/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/x86-64-accton-as5812-54t-fan.c b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/x86-64-accton-as5812-54t-fan.c new file mode 100644 index 000000000000..bad9245e93ac --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/x86-64-accton-as5812-54t-fan.c @@ -0,0 +1,442 @@ +/* + * A hwmon driver for the Accton as5812 54t fan + * + * Copyright (C) 2015 Accton Technology Corporation. + * Brandon Chuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FAN_MAX_NUMBER 5 +#define FAN_SPEED_CPLD_TO_RPM_STEP 150 +#define FAN_SPEED_PRECENT_TO_CPLD_STEP 5 +#define FAN_DUTY_CYCLE_MIN 0 +#define FAN_DUTY_CYCLE_MAX 100 /* 100% */ + +#define CPLD_REG_FAN_STATUS_OFFSET 0xC +#define CPLD_REG_FANR_STATUS_OFFSET 0x1F +#define CPLD_REG_FAN_DIRECTION_OFFSET 0x1E + +#define CPLD_FAN1_REG_SPEED_OFFSET 0x10 +#define CPLD_FAN2_REG_SPEED_OFFSET 0x11 +#define CPLD_FAN3_REG_SPEED_OFFSET 0x12 +#define CPLD_FAN4_REG_SPEED_OFFSET 0x13 +#define CPLD_FAN5_REG_SPEED_OFFSET 0x14 + +#define CPLD_FANR1_REG_SPEED_OFFSET 0x18 +#define CPLD_FANR2_REG_SPEED_OFFSET 0x19 +#define CPLD_FANR3_REG_SPEED_OFFSET 0x1A +#define CPLD_FANR4_REG_SPEED_OFFSET 0x1B +#define CPLD_FANR5_REG_SPEED_OFFSET 0x1C + +#define CPLD_REG_FAN_PWM_CYCLE_OFFSET 0xD + +#define CPLD_FAN1_INFO_BIT_MASK 0x1 +#define CPLD_FAN2_INFO_BIT_MASK 0x2 +#define CPLD_FAN3_INFO_BIT_MASK 0x4 +#define CPLD_FAN4_INFO_BIT_MASK 0x8 +#define CPLD_FAN5_INFO_BIT_MASK 0x10 + +#define PROJECT_NAME + +#define LOCAL_DEBUG 0 + +static struct accton_as5812_54t_fan *fan_data = NULL; + +struct accton_as5812_54t_fan { + struct platform_device *pdev; + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 status[FAN_MAX_NUMBER]; /* inner first fan status */ + u32 speed[FAN_MAX_NUMBER]; /* inner first fan speed */ + u8 direction[FAN_MAX_NUMBER]; /* reconrd the direction of inner first and second fans */ + u32 duty_cycle[FAN_MAX_NUMBER]; /* control the speed of inner first and second fans */ + u8 r_status[FAN_MAX_NUMBER]; /* inner second fan status */ + u32 r_speed[FAN_MAX_NUMBER]; /* inner second fan speed */ +}; + +/*******************/ +#define MAKE_FAN_MASK_OR_REG(name,type) \ + CPLD_FAN##type##1_##name, \ + CPLD_FAN##type##2_##name, \ + CPLD_FAN##type##3_##name, \ + CPLD_FAN##type##4_##name, \ + CPLD_FAN##type##5_##name, + +/* fan related data + */ +static const u8 fan_info_mask[] = { + MAKE_FAN_MASK_OR_REG(INFO_BIT_MASK,) +}; + +static const u8 fan_speed_reg[] = { + MAKE_FAN_MASK_OR_REG(REG_SPEED_OFFSET,) +}; + +static const u8 fanr_speed_reg[] = { + MAKE_FAN_MASK_OR_REG(REG_SPEED_OFFSET,R) +}; + +/*******************/ +#define DEF_FAN_SET(id) \ + FAN##id##_FAULT, \ + FAN##id##_SPEED, \ + FAN##id##_DUTY_CYCLE, \ + FAN##id##_DIRECTION, \ + FANR##id##_FAULT, \ + FANR##id##_SPEED, + +enum sysfs_fan_attributes { + DEF_FAN_SET(1) + DEF_FAN_SET(2) + DEF_FAN_SET(3) + DEF_FAN_SET(4) + DEF_FAN_SET(5) +}; +/*******************/ +static void accton_as5812_54t_fan_update_device(struct device *dev); +static int accton_as5812_54t_fan_read_value(u8 reg); +static int accton_as5812_54t_fan_write_value(u8 reg, u8 value); + +static ssize_t fan_set_duty_cycle(struct device *dev, + struct device_attribute *da,const char *buf, size_t count); +static ssize_t fan_show_value(struct device *dev, + struct device_attribute *da, char *buf); + +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + + +/*******************/ +#define _MAKE_SENSOR_DEVICE_ATTR(prj, id) \ + static SENSOR_DEVICE_ATTR(prj##fan##id##_fault, S_IRUGO, fan_show_value, NULL, FAN##id##_FAULT); \ + static SENSOR_DEVICE_ATTR(prj##fan##id##_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##id##_SPEED); \ + static SENSOR_DEVICE_ATTR(prj##fan##id##_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, \ + fan_set_duty_cycle, FAN##id##_DUTY_CYCLE); \ + static SENSOR_DEVICE_ATTR(prj##fan##id##_direction, S_IRUGO, fan_show_value, NULL, FAN##id##_DIRECTION); \ + static SENSOR_DEVICE_ATTR(prj##fanr##id##_fault, S_IRUGO, fan_show_value, NULL, FANR##id##_FAULT); \ + static SENSOR_DEVICE_ATTR(prj##fanr##id##_speed_rpm, S_IRUGO, fan_show_value, NULL, FANR##id##_SPEED); + +#define MAKE_SENSOR_DEVICE_ATTR(prj,id) _MAKE_SENSOR_DEVICE_ATTR(prj,id) + +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 1) +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 2) +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 3) +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 4) +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 5) +/*******************/ + +#define _MAKE_FAN_ATTR(prj, id) \ + &sensor_dev_attr_##prj##fan##id##_fault.dev_attr.attr, \ + &sensor_dev_attr_##prj##fan##id##_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_##prj##fan##id##_duty_cycle_percentage.dev_attr.attr,\ + &sensor_dev_attr_##prj##fan##id##_direction.dev_attr.attr, \ + &sensor_dev_attr_##prj##fanr##id##_fault.dev_attr.attr, \ + &sensor_dev_attr_##prj##fanr##id##_speed_rpm.dev_attr.attr, + +#define MAKE_FAN_ATTR(prj, id) _MAKE_FAN_ATTR(prj, id) + +static struct attribute *accton_as5812_54t_fan_attributes[] = { + /* fan related attributes */ + MAKE_FAN_ATTR(PROJECT_NAME,1) + MAKE_FAN_ATTR(PROJECT_NAME,2) + MAKE_FAN_ATTR(PROJECT_NAME,3) + MAKE_FAN_ATTR(PROJECT_NAME,4) + MAKE_FAN_ATTR(PROJECT_NAME,5) + NULL +}; +/*******************/ + +/* fan related functions + */ +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + ssize_t ret = 0; + int data_index, type_index; + + accton_as5812_54t_fan_update_device(dev); + + if (fan_data->valid == 0) { + return ret; + } + + type_index = attr->index%FAN2_FAULT; + data_index = attr->index/FAN2_FAULT; + + switch (type_index) { + case FAN1_FAULT: + ret = sprintf(buf, "%d\n", fan_data->status[data_index]); + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FAN1_SPEED: + ret = sprintf(buf, "%d\n", fan_data->speed[data_index]); + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FAN1_DUTY_CYCLE: + ret = sprintf(buf, "%d\n", fan_data->duty_cycle[data_index]); + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FAN1_DIRECTION: + ret = sprintf(buf, "%d\n", fan_data->direction[data_index]); /* presnet, need to modify*/ + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FANR1_FAULT: + ret = sprintf(buf, "%d\n", fan_data->r_status[data_index]); + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FANR1_SPEED: + ret = sprintf(buf, "%d\n", fan_data->r_speed[data_index]); + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + default: + if (LOCAL_DEBUG) + printk ("[Check !!][%s][%d] \n", __FUNCTION__, __LINE__); + break; + } + + return ret; +} +/*******************/ +static ssize_t fan_set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) { + + int error, value; + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < FAN_DUTY_CYCLE_MIN || value > FAN_DUTY_CYCLE_MAX) + return -EINVAL; + + accton_as5812_54t_fan_write_value(CPLD_REG_FAN_PWM_CYCLE_OFFSET, value/FAN_SPEED_PRECENT_TO_CPLD_STEP); + + fan_data->valid = 0; + + return count; +} + +static const struct attribute_group accton_as5812_54t_fan_group = { + .attrs = accton_as5812_54t_fan_attributes, +}; + +static int accton_as5812_54t_fan_read_value(u8 reg) +{ + return accton_i2c_cpld_read(0x60, reg); +} + +static int accton_as5812_54t_fan_write_value(u8 reg, u8 value) +{ + return accton_i2c_cpld_write(0x60, reg, value); +} + +static void accton_as5812_54t_fan_update_device(struct device *dev) +{ + int speed, r_speed, fault, r_fault, ctrl_speed, direction; + int i; + + mutex_lock(&fan_data->update_lock); + + if (LOCAL_DEBUG) + printk ("Starting accton_as5812_54t_fan update \n"); + + if (!(time_after(jiffies, fan_data->last_updated + HZ + HZ / 2) || !fan_data->valid)) { + /* do nothing */ + goto _exit; + } + + fan_data->valid = 0; + + if (LOCAL_DEBUG) + printk ("Starting accton_as5812_54t_fan update 2 \n"); + + fault = accton_as5812_54t_fan_read_value(CPLD_REG_FAN_STATUS_OFFSET); + r_fault = accton_as5812_54t_fan_read_value(CPLD_REG_FANR_STATUS_OFFSET); + direction = accton_as5812_54t_fan_read_value(CPLD_REG_FAN_DIRECTION_OFFSET); + ctrl_speed = accton_as5812_54t_fan_read_value(CPLD_REG_FAN_PWM_CYCLE_OFFSET); + + if ( (fault < 0) || (r_fault < 0) || (direction < 0) || (ctrl_speed < 0) ) + { + if (LOCAL_DEBUG) + printk ("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); + goto _exit; /* error */ + } + + if (LOCAL_DEBUG) + printk ("[fan:] fault:%d, r_fault=%d, direction=%d, ctrl_speed=%d \n",fault, r_fault, direction, ctrl_speed); + + for (i=0; istatus[i] = (fault & fan_info_mask[i]) >> i; + if (LOCAL_DEBUG) + printk ("[fan%d:] fail=%d \n",i, fan_data->status[i]); + + fan_data->r_status[i] = (r_fault & fan_info_mask[i]) >> i; + fan_data->direction[i] = (direction & fan_info_mask[i]) >> i; + fan_data->duty_cycle[i] = ctrl_speed * FAN_SPEED_PRECENT_TO_CPLD_STEP; + + /* fan speed + */ + speed = accton_as5812_54t_fan_read_value(fan_speed_reg[i]); + r_speed = accton_as5812_54t_fan_read_value(fanr_speed_reg[i]); + if ( (speed < 0) || (r_speed < 0) ) + { + if (LOCAL_DEBUG) + printk ("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); + goto _exit; /* error */ + } + + if (LOCAL_DEBUG) + printk ("[fan%d:] speed:%d, r_speed=%d \n", i, speed, r_speed); + + fan_data->speed[i] = speed * FAN_SPEED_CPLD_TO_RPM_STEP; + fan_data->r_speed[i] = r_speed * FAN_SPEED_CPLD_TO_RPM_STEP; + } + + /* finish to update */ + fan_data->last_updated = jiffies; + fan_data->valid = 1; + +_exit: + mutex_unlock(&fan_data->update_lock); +} + +static int accton_as5812_54t_fan_probe(struct platform_device *pdev) +{ + int status = -1; + + /* Register sysfs hooks */ + status = sysfs_create_group(&pdev->dev.kobj, &accton_as5812_54t_fan_group); + if (status) { + goto exit; + + } + + fan_data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(fan_data->hwmon_dev)) { + status = PTR_ERR(fan_data->hwmon_dev); + goto exit_remove; + } + + dev_info(&pdev->dev, "accton_as5812_54t_fan\n"); + + return 0; + +exit_remove: + sysfs_remove_group(&pdev->dev.kobj, &accton_as5812_54t_fan_group); +exit: + return status; +} + +static int accton_as5812_54t_fan_remove(struct platform_device *pdev) +{ + hwmon_device_unregister(fan_data->hwmon_dev); + sysfs_remove_group(&fan_data->pdev->dev.kobj, &accton_as5812_54t_fan_group); + + return 0; +} + +#define DRVNAME "as5812_54t_fan" + +static struct platform_driver accton_as5812_54t_fan_driver = { + .probe = accton_as5812_54t_fan_probe, + .remove = accton_as5812_54t_fan_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init accton_as5812_54t_fan_init(void) +{ + int ret; + + extern int platform_accton_as5812_54t(void); + if (!platform_accton_as5812_54t()) { + return -ENODEV; + } + + ret = platform_driver_register(&accton_as5812_54t_fan_driver); + if (ret < 0) { + goto exit; + } + + fan_data = kzalloc(sizeof(struct accton_as5812_54t_fan), GFP_KERNEL); + if (!fan_data) { + ret = -ENOMEM; + platform_driver_unregister(&accton_as5812_54t_fan_driver); + goto exit; + } + + mutex_init(&fan_data->update_lock); + fan_data->valid = 0; + + fan_data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(fan_data->pdev)) { + ret = PTR_ERR(fan_data->pdev); + platform_driver_unregister(&accton_as5812_54t_fan_driver); + kfree(fan_data); + goto exit; + } + +exit: + return ret; +} + +static void __exit accton_as5812_54t_fan_exit(void) +{ + platform_device_unregister(fan_data->pdev); + platform_driver_unregister(&accton_as5812_54t_fan_driver); + kfree(fan_data); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_as5812_54t_fan driver"); +MODULE_LICENSE("GPL"); + +module_init(accton_as5812_54t_fan_init); +module_exit(accton_as5812_54t_fan_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/x86-64-accton-as5812-54t-leds.c b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/x86-64-accton-as5812-54t-leds.c new file mode 100644 index 000000000000..011f62e76c06 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/x86-64-accton-as5812-54t-leds.c @@ -0,0 +1,601 @@ +/* + * A LED driver for the accton_as5812_54t_led + * + * Copyright (C) 2015 Accton Technology Corporation. + * Brandon Chuang + * + * 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. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include + +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +extern void led_classdev_unregister(struct led_classdev *led_cdev); +extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev); +extern void led_classdev_resume(struct led_classdev *led_cdev); +extern void led_classdev_suspend(struct led_classdev *led_cdev); + +#define DRVNAME "as5812_54t_led" + +struct accton_as5812_54t_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[4]; /* Register value, 0 = LOC/DIAG/FAN LED + 1 = PSU1/PSU2 LED + 2 = FAN1-4 LED + 3 = FAN5-6 LED */ +}; + +static struct accton_as5812_54t_led_data *ledctl = NULL; + +/* LED related data + */ +#define LED_TYPE_PSU1_REG_MASK 0x03 +#define LED_MODE_PSU1_GREEN_MASK 0x02 +#define LED_MODE_PSU1_AMBER_MASK 0x01 +#define LED_MODE_PSU1_OFF_MASK 0x03 +#define LED_MODE_PSU1_AUTO_MASK 0x00 + +#define LED_TYPE_PSU2_REG_MASK 0x0C +#define LED_MODE_PSU2_GREEN_MASK 0x08 +#define LED_MODE_PSU2_AMBER_MASK 0x04 +#define LED_MODE_PSU2_OFF_MASK 0x0C +#define LED_MODE_PSU2_AUTO_MASK 0x00 + +#define LED_TYPE_DIAG_REG_MASK 0x0C +#define LED_MODE_DIAG_GREEN_MASK 0x08 +#define LED_MODE_DIAG_AMBER_MASK 0x04 +#define LED_MODE_DIAG_OFF_MASK 0x0C + +#define LED_TYPE_FAN_REG_MASK 0x03 +#define LED_MODE_FAN_GREEN_MASK 0x02 +#define LED_MODE_FAN_AMBER_MASK 0x01 +#define LED_MODE_FAN_OFF_MASK 0x03 +#define LED_MODE_FAN_AUTO_MASK 0x00 + +#define LED_TYPE_FAN1_REG_MASK 0x03 +#define LED_TYPE_FAN2_REG_MASK 0x0C +#define LED_TYPE_FAN3_REG_MASK 0x30 +#define LED_TYPE_FAN4_REG_MASK 0xC0 +#define LED_TYPE_FAN5_REG_MASK 0x03 +#define LED_TYPE_FAN6_REG_MASK 0x0C + +#define LED_MODE_FANX_GREEN_MASK 0x01 +#define LED_MODE_FANX_RED_MASK 0x02 +#define LED_MODE_FANX_OFF_MASK 0x00 + +#define LED_TYPE_LOC_REG_MASK 0x30 +#define LED_MODE_LOC_ON_MASK 0x00 +#define LED_MODE_LOC_OFF_MASK 0x10 +#define LED_MODE_LOC_BLINK_MASK 0x20 + +static const u8 led_reg[] = { + 0xA, /* LOC/DIAG/FAN LED*/ + 0xB, /* PSU1/PSU2 LED */ + 0x16, /* FAN1-4 LED */ + 0x17, /* FAN4-6 LED */ +}; + +enum led_type { + LED_TYPE_PSU1, + LED_TYPE_PSU2, + LED_TYPE_DIAG, + LED_TYPE_FAN, + LED_TYPE_FAN1, + LED_TYPE_FAN2, + LED_TYPE_FAN3, + LED_TYPE_FAN4, + LED_TYPE_FAN5, + LED_TYPE_LOC +}; + +enum led_light_mode { + LED_MODE_OFF = 0, + LED_MODE_GREEN, + LED_MODE_GREEN_BLINK, + LED_MODE_AMBER, + LED_MODE_AMBER_BLINK, + LED_MODE_RED, + LED_MODE_RED_BLINK, + LED_MODE_BLUE, + LED_MODE_BLUE_BLINK, + LED_MODE_AUTO, + LED_MODE_UNKNOWN +}; + +struct led_type_mode { + enum led_type type; + int type_mask; + enum led_light_mode mode; + int mode_mask; +}; + +static struct led_type_mode led_type_mode_data[] = { +{LED_TYPE_PSU1, LED_TYPE_PSU1_REG_MASK, LED_MODE_GREEN, LED_MODE_PSU1_GREEN_MASK}, +{LED_TYPE_PSU1, LED_TYPE_PSU1_REG_MASK, LED_MODE_AMBER, LED_MODE_PSU1_AMBER_MASK}, +{LED_TYPE_PSU1, LED_TYPE_PSU1_REG_MASK, LED_MODE_AUTO, LED_MODE_PSU1_AUTO_MASK}, +{LED_TYPE_PSU1, LED_TYPE_PSU1_REG_MASK, LED_MODE_OFF, LED_MODE_PSU1_OFF_MASK}, +{LED_TYPE_PSU2, LED_TYPE_PSU2_REG_MASK, LED_MODE_GREEN, LED_MODE_PSU2_GREEN_MASK}, +{LED_TYPE_PSU2, LED_TYPE_PSU2_REG_MASK, LED_MODE_AMBER, LED_MODE_PSU2_AMBER_MASK}, +{LED_TYPE_PSU2, LED_TYPE_PSU2_REG_MASK, LED_MODE_AUTO, LED_MODE_PSU2_AUTO_MASK}, +{LED_TYPE_PSU2, LED_TYPE_PSU2_REG_MASK, LED_MODE_OFF, LED_MODE_PSU2_OFF_MASK}, +{LED_TYPE_FAN, LED_TYPE_FAN_REG_MASK, LED_MODE_GREEN, LED_MODE_FAN_GREEN_MASK}, +{LED_TYPE_FAN, LED_TYPE_FAN_REG_MASK, LED_MODE_AMBER, LED_MODE_FAN_AMBER_MASK}, +{LED_TYPE_FAN, LED_TYPE_FAN_REG_MASK, LED_MODE_AUTO, LED_MODE_FAN_AUTO_MASK}, +{LED_TYPE_FAN, LED_TYPE_FAN_REG_MASK, LED_MODE_OFF, LED_MODE_FAN_OFF_MASK}, +{LED_TYPE_FAN1, LED_TYPE_FAN1_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 0}, +{LED_TYPE_FAN1, LED_TYPE_FAN1_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 0}, +{LED_TYPE_FAN1, LED_TYPE_FAN1_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 0}, +{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 2}, +{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 2}, +{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 2}, +{LED_TYPE_FAN3, LED_TYPE_FAN3_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 4}, +{LED_TYPE_FAN3, LED_TYPE_FAN3_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 4}, +{LED_TYPE_FAN3, LED_TYPE_FAN3_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 4}, +{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 6}, +{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 6}, +{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 6}, +{LED_TYPE_FAN5, LED_TYPE_FAN5_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 0}, +{LED_TYPE_FAN5, LED_TYPE_FAN5_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 0}, +{LED_TYPE_FAN5, LED_TYPE_FAN5_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 0}, +{LED_TYPE_DIAG, LED_TYPE_DIAG_REG_MASK, LED_MODE_GREEN, LED_MODE_DIAG_GREEN_MASK}, +{LED_TYPE_DIAG, LED_TYPE_DIAG_REG_MASK, LED_MODE_AMBER, LED_MODE_DIAG_AMBER_MASK}, +{LED_TYPE_DIAG, LED_TYPE_DIAG_REG_MASK, LED_MODE_OFF, LED_MODE_DIAG_OFF_MASK}, +{LED_TYPE_LOC, LED_TYPE_LOC_REG_MASK, LED_MODE_AMBER, LED_MODE_LOC_ON_MASK}, +{LED_TYPE_LOC, LED_TYPE_LOC_REG_MASK, LED_MODE_OFF, LED_MODE_LOC_OFF_MASK}, +{LED_TYPE_LOC, LED_TYPE_LOC_REG_MASK, LED_MODE_AMBER_BLINK, LED_MODE_LOC_BLINK_MASK} +}; + + +struct fanx_info_s { + u8 cname; /* device name */ + enum led_type type; + u8 reg_id; /* map to led_reg & reg_val */ +}; + +static struct fanx_info_s fanx_info[] = { + {'1', LED_TYPE_FAN1, 2}, + {'2', LED_TYPE_FAN2, 2}, + {'3', LED_TYPE_FAN3, 2}, + {'4', LED_TYPE_FAN4, 2}, + {'5', LED_TYPE_FAN5, 3} +}; + +static int led_reg_val_to_light_mode(enum led_type type, u8 reg_val) { + int i; + + for (i = 0; i < ARRAY_SIZE(led_type_mode_data); i++) { + + if (type != led_type_mode_data[i].type) + continue; + + if ((led_type_mode_data[i].type_mask & reg_val) == + led_type_mode_data[i].mode_mask) + { + return led_type_mode_data[i].mode; + } + } + + return LED_MODE_UNKNOWN; +} + +static u8 led_light_mode_to_reg_val(enum led_type type, + enum led_light_mode mode, u8 reg_val) { + int i; + + for (i = 0; i < ARRAY_SIZE(led_type_mode_data); i++) { + if (type != led_type_mode_data[i].type) + continue; + + if (mode != led_type_mode_data[i].mode) + continue; + + reg_val = led_type_mode_data[i].mode_mask | + (reg_val & (~led_type_mode_data[i].type_mask)); + } + + return reg_val; +} + +static int accton_as5812_54t_led_read_value(u8 reg) +{ + return accton_i2c_cpld_read(0x60, reg); +} + +static int accton_as5812_54t_led_write_value(u8 reg, u8 value) +{ + return accton_i2c_cpld_write(0x60, reg, value); +} + +static void accton_as5812_54t_led_update(void) +{ + mutex_lock(&ledctl->update_lock); + + if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) + || !ledctl->valid) { + int i; + + dev_dbg(&ledctl->pdev->dev, "Starting accton_as5812_54t_led update\n"); + + /* Update LED data + */ + for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { + int status = accton_as5812_54t_led_read_value(led_reg[i]); + + if (status < 0) { + ledctl->valid = 0; + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg[i], status); + goto exit; + } + else + { + ledctl->reg_val[i] = status; + } + } + + ledctl->last_updated = jiffies; + ledctl->valid = 1; + } + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void accton_as5812_54t_led_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode, + u8 reg, enum led_type type) +{ + int reg_val; + + mutex_lock(&ledctl->update_lock); + + reg_val = accton_as5812_54t_led_read_value(reg); + + if (reg_val < 0) { + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", reg, reg_val); + goto exit; + } + + reg_val = led_light_mode_to_reg_val(type, led_light_mode, reg_val); + accton_as5812_54t_led_write_value(reg, reg_val); + + /* to prevent the slow-update issue */ + ledctl->valid = 0; + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void accton_as5812_54t_led_psu_1_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as5812_54t_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU1); +} + +static enum led_brightness accton_as5812_54t_led_psu_1_get(struct led_classdev *cdev) +{ + accton_as5812_54t_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_PSU1, ledctl->reg_val[1]); +} + +static void accton_as5812_54t_led_psu_2_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as5812_54t_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU2); +} + +static enum led_brightness accton_as5812_54t_led_psu_2_get(struct led_classdev *cdev) +{ + accton_as5812_54t_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_PSU2, ledctl->reg_val[1]); +} + +static void accton_as5812_54t_led_fan_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as5812_54t_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_FAN); +} + +static enum led_brightness accton_as5812_54t_led_fan_get(struct led_classdev *cdev) +{ + accton_as5812_54t_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_FAN, ledctl->reg_val[0]); +} + + +static void accton_as5812_54t_led_fanx_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + enum led_type led_type1; + int reg_id; + int i, nsize; + int ncount = sizeof(fanx_info)/sizeof(struct fanx_info_s); + + for(i=0;iname); + + if (led_cdev->name[nsize-1] == fanx_info[i].cname) + { + led_type1 = fanx_info[i].type; + reg_id = fanx_info[i].reg_id; + accton_as5812_54t_led_set(led_cdev, led_light_mode, led_reg[reg_id], led_type1); + return; + } + } +} + + +static enum led_brightness accton_as5812_54t_led_fanx_get(struct led_classdev *cdev) +{ + enum led_type led_type1; + int reg_id; + int i, nsize; + int ncount = sizeof(fanx_info)/sizeof(struct fanx_info_s); + + for(i=0;iname); + + if (cdev->name[nsize-1] == fanx_info[i].cname) + { + led_type1 = fanx_info[i].type; + reg_id = fanx_info[i].reg_id; + accton_as5812_54t_led_update(); + return led_reg_val_to_light_mode(led_type1, ledctl->reg_val[reg_id]); + } + } + + + return led_reg_val_to_light_mode(LED_TYPE_FAN1, ledctl->reg_val[2]); +} + + +static void accton_as5812_54t_led_diag_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as5812_54t_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_DIAG); +} + +static enum led_brightness accton_as5812_54t_led_diag_get(struct led_classdev *cdev) +{ + accton_as5812_54t_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); +} + +static void accton_as5812_54t_led_loc_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as5812_54t_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_LOC); +} + +static enum led_brightness accton_as5812_54t_led_loc_get(struct led_classdev *cdev) +{ + accton_as5812_54t_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); +} + +static struct led_classdev accton_as5812_54t_leds[] = { + [LED_TYPE_PSU1] = { + .name = "accton_as5812_54t_led::psu1", + .default_trigger = "unused", + .brightness_set = accton_as5812_54t_led_psu_1_set, + .brightness_get = accton_as5812_54t_led_psu_1_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU2] = { + .name = "accton_as5812_54t_led::psu2", + .default_trigger = "unused", + .brightness_set = accton_as5812_54t_led_psu_2_set, + .brightness_get = accton_as5812_54t_led_psu_2_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN] = { + .name = "accton_as5812_54t_led::fan", + .default_trigger = "unused", + .brightness_set = accton_as5812_54t_led_fan_set, + .brightness_get = accton_as5812_54t_led_fan_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN1] = { + .name = "accton_as5812_54t_led::fan1", + .default_trigger = "unused", + .brightness_set = accton_as5812_54t_led_fanx_set, + .brightness_get = accton_as5812_54t_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN2] = { + .name = "accton_as5812_54t_led::fan2", + .default_trigger = "unused", + .brightness_set = accton_as5812_54t_led_fanx_set, + .brightness_get = accton_as5812_54t_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN3] = { + .name = "accton_as5812_54t_led::fan3", + .default_trigger = "unused", + .brightness_set = accton_as5812_54t_led_fanx_set, + .brightness_get = accton_as5812_54t_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN4] = { + .name = "accton_as5812_54t_led::fan4", + .default_trigger = "unused", + .brightness_set = accton_as5812_54t_led_fanx_set, + .brightness_get = accton_as5812_54t_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN5] = { + .name = "accton_as5812_54t_led::fan5", + .default_trigger = "unused", + .brightness_set = accton_as5812_54t_led_fanx_set, + .brightness_get = accton_as5812_54t_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_DIAG] = { + .name = "accton_as5812_54t_led::diag", + .default_trigger = "unused", + .brightness_set = accton_as5812_54t_led_diag_set, + .brightness_get = accton_as5812_54t_led_diag_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_LOC] = { + .name = "accton_as5812_54t_led::loc", + .default_trigger = "unused", + .brightness_set = accton_as5812_54t_led_loc_set, + .brightness_get = accton_as5812_54t_led_loc_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, +}; + +static int accton_as5812_54t_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as5812_54t_leds); i++) { + led_classdev_suspend(&accton_as5812_54t_leds[i]); + } + + return 0; +} + +static int accton_as5812_54t_led_resume(struct platform_device *dev) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as5812_54t_leds); i++) { + led_classdev_resume(&accton_as5812_54t_leds[i]); + } + + return 0; +} + +static int accton_as5812_54t_led_probe(struct platform_device *pdev) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(accton_as5812_54t_leds); i++) { + ret = led_classdev_register(&pdev->dev, &accton_as5812_54t_leds[i]); + + if (ret < 0) + break; + } + + /* Check if all LEDs were successfully registered */ + if (i != ARRAY_SIZE(accton_as5812_54t_leds)){ + int j; + + /* only unregister the LEDs that were successfully registered */ + for (j = 0; j < i; j++) { + led_classdev_unregister(&accton_as5812_54t_leds[i]); + } + } + + return ret; +} + +static int accton_as5812_54t_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(accton_as5812_54t_leds); i++) { + led_classdev_unregister(&accton_as5812_54t_leds[i]); + } + + return 0; +} + +static struct platform_driver accton_as5812_54t_led_driver = { + .probe = accton_as5812_54t_led_probe, + .remove = accton_as5812_54t_led_remove, + .suspend = accton_as5812_54t_led_suspend, + .resume = accton_as5812_54t_led_resume, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init accton_as5812_54t_led_init(void) +{ + int ret; + + extern int platform_accton_as5812_54t(void); + if (!platform_accton_as5812_54t()) { + return -ENODEV; + } + + ret = platform_driver_register(&accton_as5812_54t_led_driver); + if (ret < 0) { + goto exit; + } + + ledctl = kzalloc(sizeof(struct accton_as5812_54t_led_data), GFP_KERNEL); + if (!ledctl) { + ret = -ENOMEM; + platform_driver_unregister(&accton_as5812_54t_led_driver); + goto exit; + } + + mutex_init(&ledctl->update_lock); + + ledctl->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(ledctl->pdev)) { + ret = PTR_ERR(ledctl->pdev); + platform_driver_unregister(&accton_as5812_54t_led_driver); + kfree(ledctl); + goto exit; + } + +exit: + return ret; +} + +static void __exit accton_as5812_54t_led_exit(void) +{ + platform_device_unregister(ledctl->pdev); + platform_driver_unregister(&accton_as5812_54t_led_driver); + kfree(ledctl); +} + +module_init(accton_as5812_54t_led_init); +module_exit(accton_as5812_54t_led_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_as5812_54t_led driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/x86-64-accton-as5812-54t-psu.c b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/x86-64-accton-as5812-54t-psu.c new file mode 100644 index 000000000000..a77014e877ca --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/x86-64-accton-as5812-54t-psu.c @@ -0,0 +1,372 @@ +/* + * An hwmon driver for accton as5812_54t Power Module + * + * Copyright (C) 2015 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PSU_STATUS_I2C_ADDR 0x60 +#define PSU_STATUS_I2C_REG_OFFSET 0x2 + +#define IS_POWER_GOOD(id, value) (!!(value & BIT(id*4 + 1))) +#define IS_PRESENT(id, value) (!(value & BIT(id*4))) + +static ssize_t show_index(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, char *buf); +static int as5812_54t_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +static int as5812_54t_psu_model_name_get(struct device *dev); + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as5812_54t_psu_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + u8 status; /* Status(present/power_good) register read from CPLD */ + char model_name[14]; /* Model name, read from eeprom */ +}; + +static struct as5812_54t_psu_data *as5812_54t_psu_update_device(struct device *dev); + +enum as5812_54t_psu_sysfs_attributes { + PSU_INDEX, + PSU_PRESENT, + PSU_MODEL_NAME, + PSU_POWER_GOOD +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_index, S_IRUGO, show_index, NULL, PSU_INDEX); +static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_status, NULL, PSU_PRESENT); +static SENSOR_DEVICE_ATTR(psu_model_name, S_IRUGO, show_model_name,NULL, PSU_MODEL_NAME); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD); + +static struct attribute *as5812_54t_psu_attributes[] = { + &sensor_dev_attr_psu_index.dev_attr.attr, + &sensor_dev_attr_psu_present.dev_attr.attr, + &sensor_dev_attr_psu_model_name.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + NULL +}; + +static ssize_t show_index(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5812_54t_psu_data *data = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", data->index); +} + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as5812_54t_psu_data *data = as5812_54t_psu_update_device(dev); + u8 status = 0; + + if (!data->valid) { + return sprintf(buf, "0\n"); + } + + if (attr->index == PSU_PRESENT) { + status = IS_PRESENT(data->index, data->status); + } + else { /* PSU_POWER_GOOD */ + status = IS_POWER_GOOD(data->index, data->status); + } + + return sprintf(buf, "%d\n", status); +} + +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as5812_54t_psu_data *data = as5812_54t_psu_update_device(dev); + + if (!data->valid) { + return 0; + } + + if (!IS_PRESENT(data->index, data->status)) { + return 0; + } + + if (as5812_54t_psu_model_name_get(dev) < 0) { + return -ENXIO; + } + + return sprintf(buf, "%s\n", data->model_name); +} + +static const struct attribute_group as5812_54t_psu_group = { + .attrs = as5812_54t_psu_attributes, +}; + +static int as5812_54t_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as5812_54t_psu_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as5812_54t_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as5812_54t_psu_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as5812_54t_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as5812_54t_psu_remove(struct i2c_client *client) +{ + struct as5812_54t_psu_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as5812_54t_psu_group); + kfree(data); + + return 0; +} + +enum psu_index +{ + as5812_54t_psu1, + as5812_54t_psu2 +}; + +static const struct i2c_device_id as5812_54t_psu_id[] = { + { "as5812_54t_psu1", as5812_54t_psu1 }, + { "as5812_54t_psu2", as5812_54t_psu2 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as5812_54t_psu_id); + +static struct i2c_driver as5812_54t_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as5812_54t_psu", + }, + .probe = as5812_54t_psu_probe, + .remove = as5812_54t_psu_remove, + .id_table = as5812_54t_psu_id, + .address_list = normal_i2c, +}; + +static int as5812_54t_psu_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; +} + +enum psu_type { + PSU_YM_2401_JCR, /* AC110V - F2B */ + PSU_YM_2401_JDR, /* AC110V - B2F */ + PSU_CPR_4011_4M11, /* AC110V - F2B */ + PSU_CPR_4011_4M21, /* AC110V - B2F */ + PSU_CPR_6011_2M11, /* AC110V - F2B */ + PSU_CPR_6011_2M21, /* AC110V - B2F */ + PSU_UM400D_01G, /* DC48V - F2B */ + PSU_UM400D01_01G /* DC48V - B2F */ +}; + +struct model_name_info { + enum psu_type type; + u8 offset; + u8 length; + char* model_name; +}; + +struct model_name_info models[] = { +{PSU_YM_2401_JCR, 0x20, 11, "YM-2401JCR"}, +{PSU_YM_2401_JDR, 0x20, 11, "YM-2401JDR"}, +{PSU_CPR_4011_4M11, 0x26, 13, "CPR-4011-4M11"}, +{PSU_CPR_4011_4M21, 0x26, 13, "CPR-4011-4M21"}, +{PSU_CPR_6011_2M11, 0x26, 13, "CPR-6011-2M11"}, +{PSU_CPR_6011_2M21, 0x26, 13, "CPR-6011-2M21"}, +{PSU_UM400D_01G, 0x50, 9, "um400d01G"}, +{PSU_UM400D01_01G, 0x50, 12, "um400d01-01G"}, +}; + +static int as5812_54t_psu_model_name_get(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5812_54t_psu_data *data = i2c_get_clientdata(client); + int i, status; + + for (i = 0; i < ARRAY_SIZE(models); i++) { + memset(data->model_name, 0, sizeof(data->model_name)); + + status = as5812_54t_psu_read_block(client, models[i].offset, + data->model_name, models[i].length); + if (status < 0) { + data->model_name[0] = '\0'; + dev_dbg(&client->dev, "unable to read model name from (0x%x) offset(0x%x)\n", + client->addr, models[i].offset); + return status; + } + else { + data->model_name[models[i].length] = '\0'; + } + + if (i == PSU_YM_2401_JCR || i == PSU_YM_2401_JDR) { + /* Skip the meaningless data byte 8*/ + data->model_name[8] = data->model_name[9]; + data->model_name[9] = data->model_name[10]; + data->model_name[10] = '\0'; + } + + /* Determine if the model name is known, if not, read next index + */ + if (strncmp(data->model_name, models[i].model_name, models[i].length) == 0) { + return 0; + } + else { + data->model_name[0] = '\0'; + } + } + + return -ENODATA; +} + +static struct as5812_54t_psu_data *as5812_54t_psu_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5812_54t_psu_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int status = -1; + + dev_dbg(&client->dev, "Starting as5812_54t update\n"); + data->valid = 0; + + /* Read psu status */ + status = accton_i2c_cpld_read(PSU_STATUS_I2C_ADDR, PSU_STATUS_I2C_REG_OFFSET); + + if (status < 0) { + dev_dbg(&client->dev, "cpld reg (0x%x) err %d\n", PSU_STATUS_I2C_ADDR, status); + goto exit; + } + else { + data->status = status; + } + + data->last_updated = jiffies; + data->valid = 1; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init as5812_54t_psu_init(void) +{ + extern int platform_accton_as5812_54t(void); + if (!platform_accton_as5812_54t()) { + return -ENODEV; + } + + return i2c_add_driver(&as5812_54t_psu_driver); +} + +static void __exit as5812_54t_psu_exit(void) +{ + i2c_del_driver(&as5812_54t_psu_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton as5812_54t_psu driver"); +MODULE_LICENSE("GPL"); + +module_init(as5812_54t_psu_init); +module_exit(as5812_54t_psu_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/x86-64-accton-as5812-54t-sfp.c b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/x86-64-accton-as5812-54t-sfp.c new file mode 100644 index 000000000000..88bf552de0f0 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/x86-64-accton-as5812-54t-sfp.c @@ -0,0 +1,318 @@ +/* + * An hwmon driver for accton as5812_54t sfp + * + * Copyright (C) 2015 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QSFP_PORT_START_INDEX 49 +#define BIT_INDEX(i) (1ULL << (i)) + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as5812_54t_sfp_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + int port; /* Front port index */ + char eeprom[256]; /* eeprom data */ + u8 status; /* bit0:port49, bit1:port50 and so on */ +}; + +static struct as5812_54t_sfp_data *as5812_54t_sfp_update_device(struct device *dev, int update_eeprom); +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, char *buf); +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +enum as5812_54t_sfp_sysfs_attributes { + SFP_IS_PRESENT, + SFP_PORT_NUMBER, + SFP_EEPROM, + SFP_IS_PRESENT_ALL, +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_status, NULL, SFP_IS_PRESENT); +static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, SFP_PORT_NUMBER); +static SENSOR_DEVICE_ATTR(sfp_eeprom, S_IRUGO, show_eeprom, NULL, SFP_EEPROM); +static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_status,NULL, SFP_IS_PRESENT_ALL); + +static struct attribute *as5812_54t_sfp_attributes[] = { + &sensor_dev_attr_sfp_is_present.dev_attr.attr, + &sensor_dev_attr_sfp_eeprom.dev_attr.attr, + &sensor_dev_attr_sfp_port_number.dev_attr.attr, + &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, + NULL +}; + +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5812_54t_sfp_data *data = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n",data->port); +} + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as5812_54t_sfp_data *data = as5812_54t_sfp_update_device(dev, 0); + + if (attr->index == SFP_IS_PRESENT) { + u8 val; + + val = (data->status & BIT_INDEX(data->port - QSFP_PORT_START_INDEX)) ? 0 : 1; + return sprintf(buf, "%d", val); + } + else { /* SFP_IS_PRESENT_ALL */ + return sprintf(buf, "%.2x\n", ~data->status); + } +} + +static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as5812_54t_sfp_data *data = as5812_54t_sfp_update_device(dev, 1); + + if (!data->valid) { + return 0; + } + + if ((data->status & BIT_INDEX(data->port - QSFP_PORT_START_INDEX)) != 0) { + return 0; + } + + memcpy(buf, data->eeprom, sizeof(data->eeprom)); + + return sizeof(data->eeprom); +} + +static const struct attribute_group as5812_54t_sfp_group = { + .attrs = as5812_54t_sfp_attributes, +}; + +static int as5812_54t_sfp_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as5812_54t_sfp_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as5812_54t_sfp_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + mutex_init(&data->update_lock); + data->port = dev_id->driver_data; + i2c_set_clientdata(client, data); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as5812_54t_sfp_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: sfp '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as5812_54t_sfp_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as5812_54t_sfp_remove(struct i2c_client *client) +{ + struct as5812_54t_sfp_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as5812_54t_sfp_group); + kfree(data); + + return 0; +} + +enum port_numbers { +as5812_54t_qsfp49 = 49, +as5812_54t_qsfp50, +as5812_54t_qsfp51, +as5812_54t_qsfp52, +as5812_54t_qsfp53, +as5812_54t_qsfp54 +}; + +static const struct i2c_device_id as5812_54t_sfp_id[] = { +{ "as5812_54t_qsfp49", as5812_54t_qsfp49 }, { "as5812_54t_qsfp50", as5812_54t_qsfp50 }, +{ "as5812_54t_qsfp51", as5812_54t_qsfp51 }, { "as5812_54t_qsfp52", as5812_54t_qsfp52 }, +{ "as5812_54t_qsfp53", as5812_54t_qsfp53 }, { "as5812_54t_qsfp54", as5812_54t_qsfp54 }, +{} +}; +MODULE_DEVICE_TABLE(i2c, as5812_54t_sfp_id); + +static struct i2c_driver as5812_54t_sfp_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as5812_54t_sfp", + }, + .probe = as5812_54t_sfp_probe, + .remove = as5812_54t_sfp_remove, + .id_table = as5812_54t_sfp_id, + .address_list = normal_i2c, +}; + +static int as5812_54t_sfp_read_byte(struct i2c_client *client, u8 command, u8 *data) +{ + int result = i2c_smbus_read_byte_data(client, command); + + if (unlikely(result < 0)) { + dev_dbg(&client->dev, "sfp read byte data failed, command(0x%2x), data(0x%2x)\r\n", command, result); + goto abort; + } + + *data = (u8)result; + result = 0; + +abort: + return result; +} + +static struct as5812_54t_sfp_data *as5812_54t_sfp_update_device(struct device *dev, int update_eeprom) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as5812_54t_sfp_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid || update_eeprom) { + int status = -1; + int i = 0; + + data->valid = 0; + //dev_dbg(&client->dev, "Starting as5812_54t sfp status update\n"); + data->status = 0xFF; + + /* + * Bring QSFPs out of reset, + * This is a temporary fix until the QSFP+_MOD_RST register + * can be exposed through the driver. + */ + accton_i2c_cpld_write(0x60, 0x23, 0x3F); + + /* Read present status of port 49-54(QSFP port) */ + status = accton_i2c_cpld_read(0x60, 0x22); + + if (status < 0) { + dev_dbg(&client->dev, "cpld(0x60) reg(0x22) err %d\n", status); + } + else { + data->status = status & 0x3F; /* (u32)status */ + } + + if (update_eeprom) { + /* Read eeprom data based on port number */ + memset(data->eeprom, 0, sizeof(data->eeprom)); + + /* Check if the port is present */ + if ((data->status & BIT_INDEX(data->port - QSFP_PORT_START_INDEX)) == 0) { + /* read eeprom */ + for (i = 0; i < sizeof(data->eeprom); i++) { + status = as5812_54t_sfp_read_byte(client, i, data->eeprom + i); + + if (status < 0) { + dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", + data->port); + goto exit; + } + } + } + } + + data->valid = 1; + data->last_updated = jiffies; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init as5812_54t_sfp_init(void) +{ + extern int platform_accton_as5812_54t(void); + if (!platform_accton_as5812_54t()) { + return -ENODEV; + } + + return i2c_add_driver(&as5812_54t_sfp_driver); +} + +static void __exit as5812_54t_sfp_exit(void) +{ + i2c_del_driver(&as5812_54t_sfp_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton as5812_54t_sfp driver"); +MODULE_LICENSE("GPL"); + +module_init(as5812_54t_sfp_init); +module_exit(as5812_54t_sfp_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/ym2651y.c b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/ym2651y.c new file mode 100644 index 000000000000..7101aa411f72 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/modules/ym2651y.c @@ -0,0 +1,680 @@ +/* + * An hwmon driver for the 3Y Power YM-2651Y Power Module + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_FAN_DUTY_CYCLE 100 + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +enum chips { + YM2651, + YM2401, +}; + +/* Each client has this additional data + */ +struct ym2651y_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 chip; /* chip id */ + u8 capability; /* Register value */ + u16 status_word; /* Register value */ + u8 fan_fault; /* Register value */ + u8 over_temp; /* Register value */ + u16 v_out; /* Register value */ + u16 i_out; /* Register value */ + u16 p_out; /* Register value */ + u8 vout_mode; /* Register value */ + u16 temp; /* Register value */ + u16 fan_speed; /* Register value */ + u16 fan_duty_cycle[2]; /* Register value */ + u8 fan_dir[5]; /* Register value */ + u8 pmbus_revision; /* Register value */ + u8 mfr_id[10]; /* Register value */ + u8 mfr_model[16]; /* Register value */ + u8 mfr_revsion[3]; /* Register value */ + u16 mfr_vin_min; /* Register value */ + u16 mfr_vin_max; /* Register value */ + u16 mfr_iin_max; /* Register value */ + u16 mfr_iout_max; /* Register value */ + u16 mfr_pin_max; /* Register value */ + u16 mfr_pout_max; /* Register value */ + u16 mfr_vout_min; /* Register value */ + u16 mfr_vout_max; /* Register value */ +}; + +static ssize_t show_byte(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_word(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_linear(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_vout(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_over_temp(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_ascii(struct device *dev, struct device_attribute *da, + char *buf); +static struct ym2651y_data *ym2651y_update_device(struct device *dev); +static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value); + +enum ym2651y_sysfs_attributes { + PSU_POWER_ON = 0, + PSU_TEMP_FAULT, + PSU_POWER_GOOD, + PSU_FAN1_FAULT, + PSU_FAN_DIRECTION, + PSU_OVER_TEMP, + PSU_V_OUT, + PSU_I_OUT, + PSU_P_OUT, + PSU_TEMP1_INPUT, + PSU_FAN1_SPEED, + PSU_FAN1_DUTY_CYCLE, + PSU_PMBUS_REVISION, + PSU_MFR_ID, + PSU_MFR_MODEL, + PSU_MFR_REVISION, + PSU_MFR_VIN_MIN, + PSU_MFR_VIN_MAX, + PSU_MFR_VOUT_MIN, + PSU_MFR_VOUT_MAX, + PSU_MFR_IIN_MAX, + PSU_MFR_IOUT_MAX, + PSU_MFR_PIN_MAX, + PSU_MFR_POUT_MAX +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_power_on, S_IRUGO, show_word, NULL, PSU_POWER_ON); +static SENSOR_DEVICE_ATTR(psu_temp_fault, S_IRUGO, show_word, NULL, PSU_TEMP_FAULT); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_word, NULL, PSU_POWER_GOOD); +static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_fan_fault, NULL, PSU_FAN1_FAULT); +static SENSOR_DEVICE_ATTR(psu_over_temp, S_IRUGO, show_over_temp, NULL, PSU_OVER_TEMP); +static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, show_vout, NULL, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, show_linear, NULL, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, show_linear, NULL, PSU_P_OUT); +static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); +static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IWUSR | S_IRUGO, show_linear, set_fan_duty_cycle, PSU_FAN1_DUTY_CYCLE); +static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IRUGO, show_ascii, NULL, PSU_FAN_DIRECTION); +static SENSOR_DEVICE_ATTR(psu_pmbus_revision,S_IRUGO, show_byte, NULL, PSU_PMBUS_REVISION); +static SENSOR_DEVICE_ATTR(psu_mfr_id, S_IRUGO, show_ascii, NULL, PSU_MFR_ID); +static SENSOR_DEVICE_ATTR(psu_mfr_model, S_IRUGO, show_ascii, NULL, PSU_MFR_MODEL); +static SENSOR_DEVICE_ATTR(psu_mfr_revision, S_IRUGO, show_ascii, NULL, PSU_MFR_REVISION); +static SENSOR_DEVICE_ATTR(psu_mfr_vin_min, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MIN); +static SENSOR_DEVICE_ATTR(psu_mfr_vin_max, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_vout_min, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MIN); +static SENSOR_DEVICE_ATTR(psu_mfr_vout_max, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_iin_max, S_IRUGO, show_linear, NULL, PSU_MFR_IIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_iout_max, S_IRUGO, show_linear, NULL, PSU_MFR_IOUT_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_pin_max, S_IRUGO, show_linear, NULL, PSU_MFR_PIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_pout_max, S_IRUGO, show_linear, NULL, PSU_MFR_POUT_MAX); + +static struct attribute *ym2651y_attributes[] = { + &sensor_dev_attr_psu_power_on.dev_attr.attr, + &sensor_dev_attr_psu_temp_fault.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, + &sensor_dev_attr_psu_over_temp.dev_attr.attr, + &sensor_dev_attr_psu_v_out.dev_attr.attr, + &sensor_dev_attr_psu_i_out.dev_attr.attr, + &sensor_dev_attr_psu_p_out.dev_attr.attr, + &sensor_dev_attr_psu_temp1_input.dev_attr.attr, + &sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr, + &sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr, + &sensor_dev_attr_psu_fan_dir.dev_attr.attr, + &sensor_dev_attr_psu_pmbus_revision.dev_attr.attr, + &sensor_dev_attr_psu_mfr_id.dev_attr.attr, + &sensor_dev_attr_psu_mfr_model.dev_attr.attr, + &sensor_dev_attr_psu_mfr_revision.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vin_min.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_pout_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_iin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_pin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vout_min.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vout_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_iout_max.dev_attr.attr, + NULL +}; + +static ssize_t show_byte(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + + if (!data->valid) { + return 0; + } + + return (attr->index == PSU_PMBUS_REVISION) ? sprintf(buf, "%d\n", data->pmbus_revision) : + sprintf(buf, "0\n"); +} + +static ssize_t show_word(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + u16 status = 0; + + if (!data->valid) { + return 0; + } + + switch (attr->index) { + case PSU_POWER_ON: /* psu_power_on, low byte bit 6 of status_word, 0=>ON, 1=>OFF */ + status = (data->status_word & 0x40) ? 0 : 1; + break; + case PSU_TEMP_FAULT: /* psu_temp_fault, low byte bit 2 of status_word, 0=>Normal, 1=>temp fault */ + status = (data->status_word & 0x4) >> 2; + break; + case PSU_POWER_GOOD: /* psu_power_good, high byte bit 3 of status_word, 0=>OK, 1=>FAIL */ + status = (data->status_word & 0x800) ? 0 : 1; + break; + } + + return sprintf(buf, "%d\n", status); +} + +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(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct ym2651y_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; + + mutex_lock(&data->update_lock); + data->fan_duty_cycle[nr] = speed; + ym2651y_write_word(client, 0x3B + nr, data->fan_duty_cycle[nr]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_linear(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + + u16 value = 0; + int exponent, mantissa; + int multiplier = 1000; + + if (!data->valid) { + return 0; + } + + switch (attr->index) { + case PSU_V_OUT: + value = data->v_out; + break; + case PSU_I_OUT: + value = data->i_out; + break; + case PSU_P_OUT: + value = data->p_out; + break; + case PSU_TEMP1_INPUT: + value = data->temp; + break; + case PSU_FAN1_SPEED: + value = data->fan_speed; + multiplier = 1; + break; + case PSU_FAN1_DUTY_CYCLE: + value = data->fan_duty_cycle[0]; + multiplier = 1; + break; + case PSU_MFR_VIN_MIN: + value = data->mfr_vin_min; + break; + case PSU_MFR_VIN_MAX: + value = data->mfr_vin_max; + break; + case PSU_MFR_VOUT_MIN: + value = data->mfr_vout_min; + break; + case PSU_MFR_VOUT_MAX: + value = data->mfr_vout_max; + break; + case PSU_MFR_PIN_MAX: + value = data->mfr_pin_max; + break; + case PSU_MFR_POUT_MAX: + value = data->mfr_pout_max; + break; + case PSU_MFR_IOUT_MAX: + value = data->mfr_iout_max; + break; + case PSU_MFR_IIN_MAX: + value = data->mfr_iin_max; + 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 show_fan_fault(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + u8 shift; + + if (!data->valid) { + return 0; + } + + shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6; + + return sprintf(buf, "%d\n", data->fan_fault >> shift); +} + +static ssize_t show_over_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct ym2651y_data *data = ym2651y_update_device(dev); + + if (!data->valid) { + return 0; + } + + return sprintf(buf, "%d\n", data->over_temp >> 7); +} + +static ssize_t show_ascii(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + u8 *ptr = NULL; + + if (!data->valid) { + return 0; + } + + switch (attr->index) { + case PSU_FAN_DIRECTION: /* psu_fan_dir */ + ptr = data->fan_dir + 1; /* Skip the first byte since it is the length of string. */ + break; + case PSU_MFR_ID: /* psu_mfr_id */ + ptr = data->mfr_id + 1; /* The first byte is the count byte of string. */; + break; + case PSU_MFR_MODEL: /* psu_mfr_model */ + ptr = data->mfr_model + 1; /* The first byte is the count byte of string. */ + break; + case PSU_MFR_REVISION: /* psu_mfr_revision */ + ptr = data->mfr_revsion + 1; /* The first byte is the count byte of string. */ + break; + default: + return 0; + } + + return sprintf(buf, "%s\n", ptr); +} + +static ssize_t show_vout_by_mode(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct ym2651y_data *data = ym2651y_update_device(dev); + int exponent, mantissa; + int multiplier = 1000; + + if (!data->valid) { + return 0; + } + + exponent = two_complement_to_int(data->vout_mode, 5, 0x1f); + mantissa = data->v_out; + + return (exponent > 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) : + sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); +} + +static ssize_t show_vout(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ym2651y_data *data = i2c_get_clientdata(client); + + if (data->chip == YM2401) { + return show_vout_by_mode(dev, da, buf); + } + + return show_linear(dev, da, buf); +} + +static const struct attribute_group ym2651y_group = { + .attrs = ym2651y_attributes, +}; + +static int ym2651y_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct ym2651y_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct ym2651y_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->chip = dev_id->driver_data; + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &ym2651y_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &ym2651y_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int ym2651y_remove(struct i2c_client *client) +{ + struct ym2651y_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &ym2651y_group); + kfree(data); + + return 0; +} + +static const struct i2c_device_id ym2651y_id[] = { + { "ym2651", YM2651 }, + { "ym2401", YM2401 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ym2651y_id); + +static struct i2c_driver ym2651y_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ym2651", + }, + .probe = ym2651y_probe, + .remove = ym2651y_remove, + .id_table = ym2651y_id, + .address_list = normal_i2c, +}; + +static int ym2651y_read_byte(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int ym2651y_read_word(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_data(client, reg); +} + +static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_word_data(client, reg, value); +} + +static int ym2651y_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 ym2651y_data *ym2651y_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ym2651y_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int i, status, length; + u8 command, buf; + struct reg_data_byte regs_byte[] = { {0x19, &data->capability}, + {0x20, &data->vout_mode}, + {0x7d, &data->over_temp}, + {0x81, &data->fan_fault}, + {0x98, &data->pmbus_revision}}; + struct reg_data_word regs_word[] = { {0x79, &data->status_word}, + {0x8b, &data->v_out}, + {0x8c, &data->i_out}, + {0x96, &data->p_out}, + {0x8d, &data->temp}, + {0x3b, &(data->fan_duty_cycle[0])}, + {0x3c, &(data->fan_duty_cycle[1])}, + {0x90, &data->fan_speed}, + {0xa0, &data->mfr_vin_min}, + {0xa1, &data->mfr_vin_max}, + {0xa2, &data->mfr_iin_max}, + {0xa3, &data->mfr_pin_max}, + {0xa4, &data->mfr_vout_min}, + {0xa5, &data->mfr_vout_max}, + {0xa6, &data->mfr_iout_max}, + {0xa7, &data->mfr_pout_max}}; + + dev_dbg(&client->dev, "Starting ym2651 update\n"); + data->valid = 0; + + /* Read byte data */ + for (i = 0; i < ARRAY_SIZE(regs_byte); i++) { + status = ym2651y_read_byte(client, regs_byte[i].reg); + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", + regs_byte[i].reg, status); + goto exit; + } + else { + *(regs_byte[i].value) = status; + } + } + + /* Read word data */ + for (i = 0; i < ARRAY_SIZE(regs_word); i++) { + status = ym2651y_read_word(client, regs_word[i].reg); + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", + regs_word[i].reg, status); + goto exit; + } + else { + *(regs_word[i].value) = status; + } + } + + /* Read fan_direction */ + command = 0xC3; + status = ym2651y_read_block(client, command, data->fan_dir, + ARRAY_SIZE(data->fan_dir)-1); + data->fan_dir[ARRAY_SIZE(data->fan_dir)-1] = '\0'; + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + goto exit; + } + + /* Read mfr_id */ + command = 0x99; + status = ym2651y_read_block(client, command, data->mfr_id, + ARRAY_SIZE(data->mfr_id)-1); + data->mfr_id[ARRAY_SIZE(data->mfr_id)-1] = '\0'; + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + goto exit; + } + + /* Read mfr_model */ + command = 0x9a; + length = 1; + + /* Read first byte to determine the length of data */ + status = ym2651y_read_block(client, command, &buf, length); + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + goto exit; + } + + status = ym2651y_read_block(client, command, data->mfr_model, buf+1); + data->mfr_model[buf+1] = '\0'; + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + goto exit; + } + + /* Read mfr_revsion */ + command = 0x9b; + status = ym2651y_read_block(client, command, data->mfr_revsion, + ARRAY_SIZE(data->mfr_revsion)-1); + data->mfr_revsion[ARRAY_SIZE(data->mfr_revsion)-1] = '\0'; + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + goto exit; + } + + data->last_updated = jiffies; + data->valid = 1; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init ym2651y_init(void) +{ + return i2c_add_driver(&ym2651y_driver); +} + +static void __exit ym2651y_exit(void) +{ + i2c_del_driver(&ym2651y_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("3Y Power YM-2651Y driver"); +MODULE_LICENSE("GPL"); + +module_init(ym2651y_init); +module_exit(ym2651y_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as5812-54t/service/as5812-platform-init.service b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/service/as5812-platform-init.service new file mode 100755 index 000000000000..30c1703dc40f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/service/as5812-platform-init.service @@ -0,0 +1,13 @@ +[Unit] +Description=Accton AS5712-54T Platform initialization service +Before=pmon.service +DefaultDependencies=no + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/accton_as5812_54t_util.py install +ExecStop=/usr/local/bin/accton_as5812_54t_util.py clean +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-accton/as5812-54t/utils/README b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/utils/README new file mode 100755 index 000000000000..2284eb091fcb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/utils/README @@ -0,0 +1,60 @@ +Copyright (C) 2016 Accton Networks, Inc. + +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 3 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, see . + +To initialize the system, run "accton_as5812_54t_util.py install". +To clean up the drivers & devices, run "accton_as5812_54t_util.py clean". +To dump information of sensors, run "accton_as5812_54t_util.py show". +To dump SFP EEPROM, run "accton_as5812_54t_util.py sff". +To set fan speed, run "accton_as5812_54t_util.py set fan". +To enable/disable SFP emission, run "accton_as5812_54t_util.py set sfp". +To set system LEDs' color, run "accton_as5812_54t_util.py set led" +For more information, run "accton_as5812_54t_util.py --help". + +==================================================================== +Besides applying accton_as5812_54t_util.py to access peripherals, you can +access peripherals by sysfs nodes directly after the installation is run. + +LED controls can be found under /sys/class/leds. The sysfs interface +color mappings are as follows: +Brightness: + 0 => off + 1 => green + 2 => amber + 3 => red + 4 => blue + +There are 5 system LEDs, loc, diag, fan, ps1, and ps2. +They are lit automatically by CPLD, but the loc and diag. +The loc led has only 1 color, blue. +The diag one has 3 colors: red, amber, and green. + +Fan controls can be found in /sys/bus/i2c/devices/2-0066. +There are 12 fans inside 6 fan modules. +All fans share 1 duty setting, ranged from 0~100. + +Three temperature sensors are controlled by the lm75 kernel modules. +They should already be visible under /sys/bus/i2c/drivers/lm75/. + +Two power supplies are controlled by the CPLD. +Here provide their status under +/sys/bus/i2c/devices/10-0050 and /sys/bus/i2c/devices/11-0053. + +There are 32 QSFP+ modules are equipped. +Apply "accton_as5812_54t_util.py show" to get their status. +Apply "accton_as5812_54t_util.py set sfp" to turn on/off light transmission. +Apply "accton_as5812_54t_util.py sff" to dump EEPROM information. +Before operating on that QSFP+, please make sure it is well plugged. +Otherwise, operation is going to fail. + diff --git a/platform/broadcom/sonic-platform-modules-accton/as5812-54t/utils/accton_as5812_54t_util.py b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/utils/accton_as5812_54t_util.py new file mode 100755 index 000000000000..4e9b0795081d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as5812-54t/utils/accton_as5812_54t_util.py @@ -0,0 +1,565 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 Accton Networks, Inc. +# +# 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 3 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, see . + +""" +Usage: %(scriptName)s [options] command object + +options: + -h | --help : this help message + -d | --debug : run with debug mode + -f | --force : ignore error during installation or clean +command: + install : install drivers and generate related sysfs nodes + clean : uninstall drivers and remove related sysfs nodes + show : show all systen status + sff : dump SFP eeprom + set : change board setting with fan|led|sfp +""" + +import os +import commands +import sys, getopt +import logging +import re +import time +from collections import namedtuple + + + + +PROJECT_NAME = 'as5812_54t' +version = '0.1.0' +verbose = False +DEBUG = False +args = [] +ALL_DEVICE = {} +DEVICE_NO = {'led':5, 'fan':5,'thermal':3, 'psu':2, 'sfp':6} +FORCE = 0 +logging.basicConfig(filename= PROJECT_NAME+'.log', filemode='w',level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) + + +if DEBUG == True: + print sys.argv[0] + print 'ARGV :', sys.argv[1:] + + +def main(): + global DEBUG + global args + global FORCE + + if len(sys.argv)<2: + show_help() + + options, args = getopt.getopt(sys.argv[1:], 'hdf', ['help', + 'debug', + 'force', + ]) + if DEBUG == True: + print options + print args + print len(sys.argv) + + for opt, arg in options: + if opt in ('-h', '--help'): + show_help() + elif opt in ('-d', '--debug'): + DEBUG = True + logging.basicConfig(level=logging.INFO) + elif opt in ('-f', '--force'): + FORCE = 1 + else: + logging.info('no option') + for arg in args: + if arg == 'install': + do_install() + elif arg == 'clean': + do_uninstall() + elif arg == 'show': + device_traversal() + elif arg == 'sff': + if len(args)!=2: + show_eeprom_help() + elif int(args[1]) ==0 or int(args[1]) > DEVICE_NO['sfp']: + show_eeprom_help() + else: + show_eeprom(args[1]) + return + elif arg == 'set': + if len(args)<3: + show_set_help() + else: + set_device(args[1:]) + return + else: + show_help() + + + return 0 + +def show_help(): + print __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]} + sys.exit(0) + +def show_set_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print cmd +" [led|sfp|fan]" + print " use \""+ cmd + " led 0-4 \" to set led color" + print " use \""+ cmd + " fan 0-100\" to set fan duty percetage" + print " use \""+ cmd + " sfp 1-6 {0|1}\" to set sfp# tx_disable" + sys.exit(0) + +def show_eeprom_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print " use \""+ cmd + " 1-32 \" to dump sfp# eeprom" + sys.exit(0) + +def my_log(txt): + if DEBUG == True: + print "[ROY]"+txt + return + +def log_os_system(cmd, show): + logging.info('Run :'+cmd) + status, output = commands.getstatusoutput(cmd) + my_log (cmd +"with result:" + str(status)) + my_log (" output:"+output) + if status: + logging.info('Failed :'+cmd) + if show: + print('Failed :'+cmd) + return status, output + +def driver_check(): + ret, lsmod = log_os_system("lsmod| grep accton", 0) + logging.info('mods:'+lsmod) + if len(lsmod) ==0: + return False + return True + + + self.insmod("accton_i2c_cpld") + self.insmod("cpr_4011_4mxx") + self.insmod("ym2651y") + for m in [ "sfp", "psu", "fan", "leds" ]: + self.insmod("x86-64-accton-as5812-54t-%s" % m) + +kos = [ +'modprobe i2c_dev', +'modprobe i2c_mux_pca954x force_deselect_on_exit=1', +'modprobe accton_i2c_cpld' , +'modprobe cpr_4011_4mxx' , +'modprobe ym2651y' , +'modprobe x86-64-accton-as5812-54t-sfp' , +'modprobe x86-64-accton-as5812-54t-psu' , +'modprobe x86-64-accton-as5812-54t-fan' , +'modprobe x86-64-accton-as5812-54t-leds' ] + +def driver_install(): + global FORCE + for i in range(0,len(kos)): + status, output = log_os_system(kos[i], 1) + if status: + if FORCE == 0: + return status + return 0 + +def driver_uninstall(): + global FORCE + for i in range(0,len(kos)): + rm = kos[-(i+1)].replace("modprobe", "modprobe -rq") + rm = rm.replace("insmod", "rmmod") + status, output = log_os_system(rm, 1) + if status: + if FORCE == 0: + return status + return 0 + +led_prefix ='/sys/class/leds/accton_'+PROJECT_NAME+'_led::' +hwmon_types = {'led': ['diag','fan','loc','psu1','psu2']} +hwmon_nodes = {'led': ['brightness'] } +hwmon_prefix ={'led': led_prefix} + +i2c_prefix = '/sys/bus/i2c/devices/' +i2c_bus = {'fan': ['2-0066'] , + 'thermal': ['15-0048','16-0049', '17-004a'] , + 'psu': ['11-0050','12-0053'], + 'sfp': ['-0050']} +i2c_nodes = {'fan': ['present', 'front_speed_rpm', 'rear_speed_rpm'] , + 'thermal': ['hwmon/hwmon*/temp1_input'] , + 'psu': ['psu_present ', 'psu_power_good'] , + 'sfp': ['sfp_is_present ', 'sfp_tx_disable_all']} + +sfp_map = [4,6,3,5,7,2] + +sfp_1st_index = 48 + +mknod =[ +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-1/new_device' , +'echo accton_i2c_cpld 0x60 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-15/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-16/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-17/new_device', +'echo 24c02 0x57 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo as5812_54t_psu1 0x38 > /sys/bus/i2c/devices/i2c-11/new_device', +'echo cpr_4011_4mxx 0x3c > /sys/bus/i2c/devices/i2c-11/new_device', +'echo as5812_54t_psu1 0x50 > /sys/bus/i2c/devices/i2c-11/new_device', +'echo ym2401 0x58 > /sys/bus/i2c/devices/i2c-11/new_device', +'echo as5812_54t_psu2 0x3b > /sys/bus/i2c/devices/i2c-12/new_device', +'echo cpr_4011_4mxx 0x3f > /sys/bus/i2c/devices/i2c-12/new_device', +'echo as5812_54t_psu2 0x53 > /sys/bus/i2c/devices/i2c-12/new_device', +'echo ym2401 0x5b > /sys/bus/i2c/devices/i2c-12/new_device'] + +mknod2 =[ +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-0/new_device' , +'echo accton_i2c_cpld 0x60 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-15/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-16/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-17/new_device', +'echo 24c02 0x57 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo as5812_54t_psu1 0x38 > /sys/bus/i2c/devices/i2c-11/new_device', +'echo cpr_4011_4mxx 0x3c > /sys/bus/i2c/devices/i2c-11/new_device', +'echo as5812_54t_psu1 0x50 > /sys/bus/i2c/devices/i2c-11/new_device', +'echo ym2401 0x58 > /sys/bus/i2c/devices/i2c-11/new_device', +'echo as5812_54t_psu2 0x3b > /sys/bus/i2c/devices/i2c-12/new_device', +'echo cpr_4011_4mxx 0x3f > /sys/bus/i2c/devices/i2c-12/new_device', +'echo as5812_54t_psu2 0x53 > /sys/bus/i2c/devices/i2c-12/new_device', +'echo ym2401 0x5b > /sys/bus/i2c/devices/i2c-12/new_device'] + + + +def i2c_order_check(): + # i2c bus 0 and 1 might be installed in different order. + # Here check if 0x71 is exist @ i2c-0 + tmp = "echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-0/new_device" + status, output = log_os_system(tmp, 0) + if not device_exist(): + order = 1 + else: + order = 0 + tmp = "echo 0x71 > /sys/bus/i2c/devices/i2c-0/delete_device" + status, output = log_os_system(tmp, 0) + return order + +def device_install(): + global FORCE + + order = i2c_order_check() + + # if 0x71 is not exist @i2c-0, use reversed bus order + if order: + for i in range(0,len(mknod2)): + #for pca954x need times to built new i2c buses + if mknod2[i].find('pca954') != -1: + time.sleep(1) + + status, output = log_os_system(mknod2[i], 1) + if status: + print output + if FORCE == 0: + return status + else: + for i in range(0,len(mknod)): + #for pca954x need times to built new i2c buses + if mknod[i].find('pca954') != -1: + time.sleep(1) + + status, output = log_os_system(mknod[i], 1) + if status: + print output + if FORCE == 0: + return status + for i in range(sfp_1st_index,len(sfp_map)): + status, output =log_os_system("echo sfp"+str(i+1)+" 0x50 > /sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/new_device", 1) + if status: + print output + if FORCE == 0: + return status + return + +def device_uninstall(): + global FORCE + + status, output =log_os_system("ls /sys/bus/i2c/devices/1-0071", 0) + if status==0: + I2C_ORDER=1 + else: + I2C_ORDER=0 + + for i in range(sfp_1st_index,len(sfp_map)): + target = "/sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/delete_device" + status, output =log_os_system("echo 0x50 > "+ target, 1) + if status: + print output + if FORCE == 0: + return status + + if I2C_ORDER==0: + nodelist = mknod + else: + nodelist = mknod2 + + for i in range(len(nodelist)): + target = nodelist[-(i+1)] + temp = target.split() + del temp[1] + temp[-1] = temp[-1].replace('new_device', 'delete_device') + status, output = log_os_system(" ".join(temp), 1) + if status: + print output + if FORCE == 0: + return status + + return + +def system_ready(): + if driver_check() == False: + return False + if not device_exist(): + return False + return True + +def do_install(): + print "Checking system...." + if driver_check() == False: + print "No driver, installing...." + status = driver_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" drivers detected...." + if not device_exist(): + print "No device, installing...." + status = device_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" devices detected...." + return + +def do_uninstall(): + print "Checking system...." + if not device_exist(): + print PROJECT_NAME.upper() +" has no device installed...." + else: + print "Removing device...." + status = device_uninstall() + if status: + if FORCE == 0: + return status + + if driver_check()== False : + print PROJECT_NAME.upper() +" has no driver installed...." + else: + print "Removing installed driver...." + status = driver_uninstall() + if status: + if FORCE == 0: + return status + + return + +def devices_info(): + global DEVICE_NO + global ALL_DEVICE + global i2c_bus, hwmon_types + for key in DEVICE_NO: + ALL_DEVICE[key]= {} + for i in range(0,DEVICE_NO[key]): + ALL_DEVICE[key][key+str(i+1)] = [] + + for key in i2c_bus: + buses = i2c_bus[key] + nodes = i2c_nodes[key] + for i in range(0,len(buses)): + for j in range(0,len(nodes)): + if 'fan' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ buses[i]+"/fan"+str(k+1)+"_"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + elif 'sfp' == key: + for k in range(sfp_1st_index,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ str(sfp_map[k])+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + else: + node = key+str(i+1) + path = i2c_prefix+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + + for key in hwmon_types: + itypes = hwmon_types[key] + nodes = hwmon_nodes[key] + for i in range(0,len(itypes)): + for j in range(0,len(nodes)): + node = key+"_"+itypes[i] + path = hwmon_prefix[key]+ itypes[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][ key+str(i+1)].append(path) + + #show dict all in the order + if DEBUG == True: + for i in sorted(ALL_DEVICE.keys()): + print(i+": ") + for j in sorted(ALL_DEVICE[i].keys()): + print(" "+j) + for k in (ALL_DEVICE[i][j]): + print(" "+" "+k) + return + +def show_eeprom(index): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + node = ALL_DEVICE['sfp'] ['sfp'+str(index)][0] + node = node.replace(node.split("/")[-1], 'sfp_eeprom') + # check if got hexdump command in current environment + ret, log = log_os_system("which hexdump", 0) + ret, log2 = log_os_system("which busybox hexdump", 0) + if len(log): + hex_cmd = 'hexdump' + elif len(log2): + hex_cmd = ' busybox hexdump' + else: + log = 'Failed : no hexdump cmd!!' + logging.info(log) + print log + return 1 + + print node + ":" + ret, log = log_os_system("cat "+node+"| "+hex_cmd+" -C", 1) + if ret==0: + print log + else: + print "**********device no found**********" + return + +def set_device(args): + global DEVICE_NO + global ALL_DEVICE + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + + if args[0]=='led': + if int(args[1])>4: + show_set_help() + return + #print ALL_DEVICE['led'] + for i in range(0,len(ALL_DEVICE['led'])): + for k in (ALL_DEVICE['led']['led'+str(i+1)]): + ret, log = log_os_system("echo "+args[1]+" >"+k, 1) + if ret: + return ret + elif args[0]=='fan': + if int(args[1])>100: + show_set_help() + return + #print ALL_DEVICE['fan'] + #fan1~6 is all fine, all fan share same setting + node = ALL_DEVICE['fan'] ['fan1'][0] + node = node.replace(node.split("/")[-1], 'fan_duty_cycle_percentage') + ret, log = log_os_system("cat "+ node, 1) + if ret==0: + print ("Previous fan duty: " + log.strip() +"%") + ret, log = log_os_system("echo "+args[1]+" >"+node, 1) + if ret==0: + print ("Current fan duty: " + args[1] +"%") + return ret + elif args[0]=='sfp': + if int(args[1])> DEVICE_NO[args[0]] or int(args[1])==0: + show_set_help() + return + if len(args)<2: + show_set_help() + return + + if int(args[2])>1: + show_set_help() + return + + #print ALL_DEVICE[args[0]] + for i in range(0,len(ALL_DEVICE[args[0]])): + for j in ALL_DEVICE[args[0]][args[0]+str(args[1])]: + if j.find('tx_disable')!= -1: + ret, log = log_os_system("echo "+args[2]+" >"+ j, 1) + if ret: + return ret + + return + +#get digits inside a string. +#Ex: 31 for "sfp31" +def get_value(input): + digit = re.findall('\d+', input) + return int(digit[0]) + +def device_traversal(): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + for i in sorted(ALL_DEVICE.keys()): + print("============================================") + print(i.upper()+": ") + print("============================================") + + for j in sorted(ALL_DEVICE[i].keys(), key=get_value): + print " "+j+":", + for k in (ALL_DEVICE[i][j]): + ret, log = log_os_system("cat "+k, 0) + func = k.split("/")[-1].strip() + func = re.sub(j+'_','',func,1) + func = re.sub(i.lower()+'_','',func,1) + if ret==0: + print func+"="+log+" ", + else: + print func+"="+"X"+" ", + print + print("----------------------------------------------------------------") + + + print + return + +def device_exist(): + ret1, log = log_os_system("ls "+i2c_prefix+"*0071", 0) + ret2, log = log_os_system("ls "+i2c_prefix+"i2c-2", 0) + return not(ret1 or ret2) + +if __name__ == "__main__": + main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/classes/__init__.py b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/classes/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/classes/fanutil.py b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/classes/fanutil.py new file mode 100755 index 000000000000..207436c1fc96 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/classes/fanutil.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# 1/10/2018: Jostar modify for as7716_32 +# 2/27/2018: Roy Lee modify for as7312_54x +# ------------------------------------------------------------------ + +try: + import time + import logging + from collections import namedtuple +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + + +class FanUtil(object): + """Platform-specific FanUtil class""" + + FAN_NUM_ON_MAIN_BROAD = 6 + FAN_NUM_1_IDX = 1 + FAN_NUM_2_IDX = 2 + FAN_NUM_3_IDX = 3 + FAN_NUM_4_IDX = 4 + FAN_NUM_5_IDX = 5 + FAN_NUM_6_IDX = 6 + + FAN_NODE_NUM_OF_MAP = 2 + FAN_NODE_FAULT_IDX_OF_MAP = 1 + #FAN_NODE_SPEED_IDX_OF_MAP = 2 + FAN_NODE_DIR_IDX_OF_MAP = 2 + #FAN_NODE_DUTY_IDX_OF_MAP = 4 + #FANR_NODE_FAULT_IDX_OF_MAP = 5 + + #BASE_VAL_PATH = '/sys/devices/platform/as5712_54x_fan/{0}' + BASE_VAL_PATH = '/sys/bus/i2c/devices/2-0066/{0}' + FAN_DUTY_PATH = '/sys/bus/i2c/devices/2-0066/fan_duty_cycle_percentage' + + #logfile = '' + #loglevel = logging.INFO + + """ Dictionary where + key1 = fan id index (integer) starting from 1 + key2 = fan node index (interger) starting from 1 + value = path to fan device file (string) """ + _fan_to_device_path_mapping = {} + +#fan1_direction +#fan1_fault +#fan1_present + + #(FAN_NUM_2_IDX, FAN_NODE_DUTY_IDX_OF_MAP): 'fan2_duty_cycle_percentage', + _fan_to_device_node_mapping = { + (FAN_NUM_1_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan1_fault', + (FAN_NUM_1_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan1_direction', + + (FAN_NUM_2_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan2_fault', + (FAN_NUM_2_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan2_direction', + + (FAN_NUM_3_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan3_fault', + (FAN_NUM_3_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan3_direction', + + (FAN_NUM_4_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan4_fault', + (FAN_NUM_4_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan4_direction', + + (FAN_NUM_5_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan5_fault', + (FAN_NUM_5_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan5_direction', + + (FAN_NUM_6_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan6_fault', + (FAN_NUM_6_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan6_direction', + } + + def _get_fan_to_device_node(self, fan_num, node_num): + return self._fan_to_device_node_mapping[(fan_num, node_num)] + + def _get_fan_node_val(self, fan_num, node_num): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num:%d', fan_num) + return None + + if node_num < self.FAN_NODE_FAULT_IDX_OF_MAP or node_num > self.FAN_NODE_NUM_OF_MAP: + logging.debug('GET. Parameter error. node_num:%d', node_num) + return None + + device_path = self.get_fan_to_device_path(fan_num, node_num) + + try: + val_file = open(device_path, 'r') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + content = val_file.readline().rstrip() + + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return int(content) + + def _set_fan_node_val(self, fan_num, node_num, val): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num:%d', fan_num) + return None + + if node_num < self.FAN_NODE_FAULT_IDX_OF_MAP or node_num > self.FAN_NODE_NUM_OF_MAP: + logging.debug('GET. Parameter error. node_num:%d', node_num) + return None + + content = str(val) + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + device_path = self.get_fan_to_device_path(fan_num, node_num) + try: + val_file = open(device_path, 'w') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + val_file.write(content) + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return True + + def __init__(self): + fan_path = self.BASE_VAL_PATH + + for fan_num in range(self.FAN_NUM_1_IDX, self.FAN_NUM_ON_MAIN_BROAD+1): + for node_num in range(self.FAN_NODE_FAULT_IDX_OF_MAP, self.FAN_NODE_NUM_OF_MAP+1): + self._fan_to_device_path_mapping[(fan_num, node_num)] = fan_path.format( + self._fan_to_device_node_mapping[(fan_num, node_num)]) + + def get_num_fans(self): + return self.FAN_NUM_ON_MAIN_BROAD + + def get_idx_fan_start(self): + return self.FAN_NUM_1_IDX + + def get_num_nodes(self): + return self.FAN_NODE_NUM_OF_MAP + + def get_idx_node_start(self): + return self.FAN_NODE_FAULT_IDX_OF_MAP + + def get_size_node_map(self): + return len(self._fan_to_device_node_mapping) + + def get_size_path_map(self): + return len(self._fan_to_device_path_mapping) + + def get_fan_to_device_path(self, fan_num, node_num): + return self._fan_to_device_path_mapping[(fan_num, node_num)] + + def get_fan_fault(self, fan_num): + return self._get_fan_node_val(fan_num, self.FAN_NODE_FAULT_IDX_OF_MAP) + + #def get_fan_speed(self, fan_num): + # return self._get_fan_node_val(fan_num, self.FAN_NODE_SPEED_IDX_OF_MAP) + + def get_fan_dir(self, fan_num): + return self._get_fan_node_val(fan_num, self.FAN_NODE_DIR_IDX_OF_MAP) + + def get_fan_duty_cycle(self): + #duty_path = self.FAN_DUTY_PATH + try: + val_file = open(self.FAN_DUTY_PATH) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + content = val_file.readline().rstrip() + val_file.close() + + return int(content) + #self._get_fan_node_val(fan_num, self.FAN_NODE_DUTY_IDX_OF_MAP) +#static u32 reg_val_to_duty_cycle(u8 reg_val) +#{ +# reg_val &= FAN_DUTY_CYCLE_REG_MASK; +# return ((u32)(reg_val+1) * 625 + 75)/ 100; +#} +# + def set_fan_duty_cycle(self, val): + + try: + fan_file = open(self.FAN_DUTY_PATH, 'r+') + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + #val = ((val + 1 ) * 625 +75 ) / 100 + fan_file.write(str(val)) + fan_file.close() + return True + + #def get_fanr_fault(self, fan_num): + # return self._get_fan_node_val(fan_num, self.FANR_NODE_FAULT_IDX_OF_MAP) + + def get_fanr_speed(self, fan_num): + return self._get_fan_node_val(fan_num, self.FANR_NODE_SPEED_IDX_OF_MAP) + + def get_fan_status(self, fan_num): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num, %d', fan_num) + return None + + if self.get_fan_fault(fan_num) is not None and self.get_fan_fault(fan_num) > 0: + logging.debug('GET. FAN fault. fan_num, %d', fan_num) + return False + + #if self.get_fanr_fault(fan_num) is not None and self.get_fanr_fault(fan_num) > 0: + # logging.debug('GET. FANR fault. fan_num, %d', fan_num) + # return False + + return True + +#def main(): +# fan = FanUtil() +# +# print 'get_size_node_map : %d' % fan.get_size_node_map() +# print 'get_size_path_map : %d' % fan.get_size_path_map() +# for x in range(fan.get_idx_fan_start(), fan.get_num_fans()+1): +# for y in range(fan.get_idx_node_start(), fan.get_num_nodes()+1): +# print fan.get_fan_to_device_path(x, y) +# +#if __name__ == '__main__': +# main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/classes/thermalutil.py b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/classes/thermalutil.py new file mode 100755 index 000000000000..68f0ef900df6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/classes/thermalutil.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# 1/10/2018:Jostar modify for as7716_32x +# 2/27/2018: Roy Lee modify for as7312_54x +# ------------------------------------------------------------------ + +try: + import time + import logging + import glob + from collections import namedtuple +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + + +class ThermalUtil(object): + """Platform-specific ThermalUtil class""" + + THERMAL_NUM_ON_MAIN_BROAD = 3 + THERMAL_NUM_1_IDX = 1 # 1_ON_MAIN_BROAD + THERMAL_NUM_2_IDX = 2 # 2_ON_MAIN_BROAD + THERMAL_NUM_3_IDX = 3 # 3_ON_MAIN_BROAD + + BASE_VAL_PATH = '/sys/bus/i2c/devices/{0}-00{1}/hwmon/hwmon*/temp1_input' + + """ Dictionary where + key1 = thermal id index (integer) starting from 1 + value = path to fan device file (string) """ + _thermal_to_device_path_mapping = {} + + _thermal_to_device_node_mapping = { + THERMAL_NUM_1_IDX: ['3', '48'], + THERMAL_NUM_2_IDX: ['3', '49'], + THERMAL_NUM_3_IDX: ['3', '4a'], + } + + def __init__(self): + thermal_path = self.BASE_VAL_PATH + + for x in range(self.THERMAL_NUM_1_IDX, self.THERMAL_NUM_ON_MAIN_BROAD+1): + self._thermal_to_device_path_mapping[x] = thermal_path.format( + self._thermal_to_device_node_mapping[x][0], + self._thermal_to_device_node_mapping[x][1]) + + def _get_thermal_node_val(self, thermal_num): + if thermal_num < self.THERMAL_NUM_1_IDX or thermal_num > self.THERMAL_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. thermal_num, %d', thermal_num) + return None + + device_path = self.get_thermal_to_device_path(thermal_num) + for filename in glob.glob(device_path): + try: + val_file = open(filename, 'r') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + content = val_file.readline().rstrip() + + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return int(content) + + + def get_num_thermals(self): + return self.THERMAL_NUM_ON_MAIN_BROAD + + def get_idx_thermal_start(self): + return self.THERMAL_NUM_1_IDX + + def get_size_node_map(self): + return len(self._thermal_to_device_node_mapping) + + def get_size_path_map(self): + return len(self._thermal_to_device_path_mapping) + + def get_thermal_to_device_path(self, thermal_num): + return self._thermal_to_device_path_mapping[thermal_num] + + def get_thermal_1_val(self): + return self._get_thermal_node_val(self.THERMAL_NUM_1_IDX) + + def get_thermal_2_val(self): + return self._get_thermal_node_val(self.THERMAL_NUM_2_IDX) + def get_thermal_temp(self): + return (self._get_thermal_node_val(self.THERMAL_NUM_1_IDX) + self._get_thermal_node_val(self.THERMAL_NUM_2_IDX) +self._get_thermal_node_val(self.THERMAL_NUM_3_IDX)) + +#def main(): +# thermal = ThermalUtil() +# +# print 'get_size_node_map : %d' % thermal.get_size_node_map() +# print 'get_size_path_map : %d' % thermal.get_size_path_map() +# for x in range(thermal.get_idx_thermal_start(), thermal.get_num_thermals()+1): +# print thermal.get_thermal_to_device_path(x) +# +#if __name__ == '__main__': +# main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/Makefile b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/Makefile new file mode 100755 index 000000000000..488fdb321cac --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/Makefile @@ -0,0 +1,17 @@ +ifneq ($(KERNELRELEASE),) +obj-m:= accton_i2c_cpld.o \ + accton_as7312_54x_fan.o accton_as7312_54x_leds.o \ + accton_as7312_54x_psu.o ym2651y.o + +else +ifeq (,$(KERNEL_SRC)) +$(error KERNEL_SRC is not defined) +else +KERNELDIR:=$(KERNEL_SRC) +endif +PWD:=$(shell pwd) +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules +clean: + rm -rf *.o *.mod.o *.mod.o *.ko .*cmd .tmp_versions Module.markers Module.symvers modules.order +endif diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_as7312_54x_fan.c b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_as7312_54x_fan.c new file mode 100755 index 000000000000..8764ec8a3176 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_as7312_54x_fan.c @@ -0,0 +1,815 @@ +/* + * A hwmon driver for the Accton as7312 54x fan + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as7312_54x_fan" + +#define NUM_THERMAL_SENSORS (3) /* Get sum of this number of sensors.*/ +#define THERMAL_SENSORS_DRIVER "lm75" +#define THERMAL_SENSORS_ADDRS {0x48, 0x49, 0x4a} + +#define IN +#define OUT + +static struct as7312_54x_fan_data *as7312_54x_fan_update_device(struct device *dev); +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t get_enable(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t get_sys_temp(struct device *dev, struct device_attribute *da, char *buf); +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +/* fan related data, the index should match sysfs_fan_attributes + */ +static const u8 fan_reg[] = { + 0x0F, /* fan 1-6 present status */ + 0x10, /* fan 1-6 direction(0:F2B 1:B2F) */ + 0x11, /* fan PWM(for all fan) */ + 0x12, /* front fan 1 speed(rpm) */ + 0x13, /* front fan 2 speed(rpm) */ + 0x14, /* front fan 3 speed(rpm) */ + 0x15, /* front fan 4 speed(rpm) */ + 0x16, /* front fan 5 speed(rpm) */ + 0x17, /* front fan 6 speed(rpm) */ + 0x22, /* rear fan 1 speed(rpm) */ + 0x23, /* rear fan 2 speed(rpm) */ + 0x24, /* rear fan 3 speed(rpm) */ + 0x25, /* rear fan 4 speed(rpm) */ + 0x26, /* rear fan 5 speed(rpm) */ + 0x27, /* rear fan 6 speed(rpm) */ +}; + +/* Each client has this additional data */ +struct as7312_54x_fan_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[ARRAY_SIZE(fan_reg)]; /* Register value */ + u8 enable; + int system_temp; /*In unit of mini-Celsius*/ + int sensors_found; +}; + +enum fan_id { + FAN1_ID, + FAN2_ID, + FAN3_ID, + FAN4_ID, + FAN5_ID, + FAN6_ID +}; + +enum sysfs_fan_attributes { + FAN_PRESENT_REG, + FAN_DIRECTION_REG, + FAN_DUTY_CYCLE_PERCENTAGE, /* Only one CPLD register to control duty cycle for all fans */ + FAN1_FRONT_SPEED_RPM, + FAN2_FRONT_SPEED_RPM, + FAN3_FRONT_SPEED_RPM, + FAN4_FRONT_SPEED_RPM, + FAN5_FRONT_SPEED_RPM, + FAN6_FRONT_SPEED_RPM, + FAN1_REAR_SPEED_RPM, + FAN2_REAR_SPEED_RPM, + FAN3_REAR_SPEED_RPM, + FAN4_REAR_SPEED_RPM, + FAN5_REAR_SPEED_RPM, + FAN6_REAR_SPEED_RPM, + FAN1_DIRECTION, + FAN2_DIRECTION, + FAN3_DIRECTION, + FAN4_DIRECTION, + FAN5_DIRECTION, + FAN6_DIRECTION, + FAN1_PRESENT, + FAN2_PRESENT, + FAN3_PRESENT, + FAN4_PRESENT, + FAN5_PRESENT, + FAN6_PRESENT, + FAN1_FAULT, + FAN2_FAULT, + FAN3_FAULT, + FAN4_FAULT, + FAN5_FAULT, + FAN6_FAULT +}; + +/* Define attributes + */ +#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT);\ + static SENSOR_DEVICE_ATTR(fan##index2##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT) +#define DECLARE_FAN_FAULT_ATTR(index, index2) &sensor_dev_attr_fan##index##_fault.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_fault.dev_attr.attr + +#define DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_direction, S_IRUGO, fan_show_value, NULL, FAN##index##_DIRECTION) +#define DECLARE_FAN_DIRECTION_ATTR(index) &sensor_dev_attr_fan##index##_direction.dev_attr.attr + +#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN_DUTY_CYCLE_PERCENTAGE);\ + static SENSOR_DEVICE_ATTR(pwm##index, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN_DUTY_CYCLE_PERCENTAGE);\ + static SENSOR_DEVICE_ATTR(pwm##index##_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, FAN_DUTY_CYCLE_PERCENTAGE) + +#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) &sensor_dev_attr_fan_duty_cycle_percentage.dev_attr.attr, \ + &sensor_dev_attr_pwm##index.dev_attr.attr, \ + &sensor_dev_attr_pwm##index##_enable.dev_attr.attr + +#define DECLARE_FAN_SYSTEM_TEMP_SENSOR_DEV_ATTR() \ + static SENSOR_DEVICE_ATTR(sys_temp, S_IRUGO, get_sys_temp, NULL, FAN_DUTY_CYCLE_PERCENTAGE) + +#define DECLARE_FAN_SYSTEM_TEMP_ATTR() &sensor_dev_attr_sys_temp.dev_attr.attr + + +#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, fan_show_value, NULL, FAN##index##_PRESENT) +#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr + +#define DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_front_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_rear_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_input, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index2##_input, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM) +#define DECLARE_FAN_SPEED_RPM_ATTR(index, index2) &sensor_dev_attr_fan##index##_front_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_rear_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_input.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_input.dev_attr.attr + +/* 6 fan fault attributes in this platform */ +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1,11); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2,12); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(3,13); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(4,14); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(5,15); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(6,16); +/* 6 fan speed(rpm) attributes in this platform */ +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1,11); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2,12); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(3,13); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(4,14); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(5,15); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(6,16); +/* 6 fan present attributes in this platform */ +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(5); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(6); +/* 6 fan direction attribute in this platform */ +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(1); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(2); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(3); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(4); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(5); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(6); +/* 1 fan duty cycle attribute in this platform */ +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(1); +/* System temperature for fancontrol */ +DECLARE_FAN_SYSTEM_TEMP_SENSOR_DEV_ATTR(); + +static struct attribute *as7312_54x_fan_attributes[] = { + /* fan related attributes */ + DECLARE_FAN_FAULT_ATTR(1,11), + DECLARE_FAN_FAULT_ATTR(2,12), + DECLARE_FAN_FAULT_ATTR(3,13), + DECLARE_FAN_FAULT_ATTR(4,14), + DECLARE_FAN_FAULT_ATTR(5,15), + DECLARE_FAN_FAULT_ATTR(6,16), + DECLARE_FAN_SPEED_RPM_ATTR(1,11), + DECLARE_FAN_SPEED_RPM_ATTR(2,12), + DECLARE_FAN_SPEED_RPM_ATTR(3,13), + DECLARE_FAN_SPEED_RPM_ATTR(4,14), + DECLARE_FAN_SPEED_RPM_ATTR(5,15), + DECLARE_FAN_SPEED_RPM_ATTR(6,16), + DECLARE_FAN_PRESENT_ATTR(1), + DECLARE_FAN_PRESENT_ATTR(2), + DECLARE_FAN_PRESENT_ATTR(3), + DECLARE_FAN_PRESENT_ATTR(4), + DECLARE_FAN_PRESENT_ATTR(5), + DECLARE_FAN_PRESENT_ATTR(6), + DECLARE_FAN_DIRECTION_ATTR(1), + DECLARE_FAN_DIRECTION_ATTR(2), + DECLARE_FAN_DIRECTION_ATTR(3), + DECLARE_FAN_DIRECTION_ATTR(4), + DECLARE_FAN_DIRECTION_ATTR(5), + DECLARE_FAN_DIRECTION_ATTR(6), + DECLARE_FAN_DUTY_CYCLE_ATTR(1), + DECLARE_FAN_SYSTEM_TEMP_ATTR(), + NULL +}; + +#define FAN_DUTY_CYCLE_REG_MASK 0xF +#define FAN_MAX_DUTY_CYCLE 100 +#define FAN_REG_VAL_TO_SPEED_RPM_STEP 100 + +static int as7312_54x_fan_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int as7312_54x_fan_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* fan utility functions + */ +static u32 reg_val_to_duty_cycle(u8 reg_val) +{ + reg_val &= FAN_DUTY_CYCLE_REG_MASK; + return ((u32)(reg_val+1) * 625 + 75)/ 100; +} + +static u8 duty_cycle_to_reg_val(u8 duty_cycle) +{ + return ((u32)duty_cycle * 100 / 625) - 1; +} + +static u32 reg_val_to_speed_rpm(u8 reg_val) +{ + return (u32)reg_val * FAN_REG_VAL_TO_SPEED_RPM_STEP; +} + +static u8 reg_val_to_direction(u8 reg_val, enum fan_id id) +{ + u8 mask = (1 << id); + + reg_val &= mask; + + return reg_val ? 1 : 0; +} +static u8 reg_val_to_is_present(u8 reg_val, enum fan_id id) +{ + u8 mask = (1 << id); + + reg_val &= mask; + + return reg_val ? 0 : 1; +} + +static u8 is_fan_fault(struct as7312_54x_fan_data *data, enum fan_id id) +{ + u8 ret = 1; + int front_fan_index = FAN1_FRONT_SPEED_RPM + id; + int rear_fan_index = FAN1_REAR_SPEED_RPM + id; + + /* Check if the speed of front or rear fan is ZERO, + */ + if (reg_val_to_speed_rpm(data->reg_val[front_fan_index]) && + reg_val_to_speed_rpm(data->reg_val[rear_fan_index])) { + ret = 0; + } + + return ret; +} + +static ssize_t set_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct as7312_54x_fan_data *data = as7312_54x_fan_update_device(dev); + int error, value; + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < 0 || value > 1) + return -EINVAL; + + data->enable = value; + if (value == 0) + { + return set_duty_cycle(dev, da, buf, FAN_MAX_DUTY_CYCLE); + } + return count; +} + + +static ssize_t get_enable(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as7312_54x_fan_data *data = as7312_54x_fan_update_device(dev); + + return sprintf(buf, "%u\n", data->enable); +} +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int error, value; + struct i2c_client *client = to_i2c_client(dev); + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < 0) + return -EINVAL; + + value = (value > FAN_MAX_DUTY_CYCLE)? FAN_MAX_DUTY_CYCLE : value; + + as7312_54x_fan_write_value(client, 0x33, 0); /* Disable fan speed watch dog */ + as7312_54x_fan_write_value(client, fan_reg[FAN_DUTY_CYCLE_PERCENTAGE], duty_cycle_to_reg_val(value)); + return count; +} + +/* Due to this struct is declared at lm75.c, it cannot be include + * under Sonic environment. I duplicate it from lm75.c. + */ +struct lm75_data { + struct i2c_client *client; + struct device *hwmon_dev; + struct thermal_zone_device *tz; + struct mutex update_lock; + u8 orig_conf; + u8 resolution; /* In bits, between 9 and 12 */ + u8 resolution_limits; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long sample_time; /* In jiffies */ + s16 temp[3]; /* Register values, + 0 = input + 1 = max + 2 = hyst */ +}; + +/*Copied from lm75.c*/ +static inline long lm75_reg_to_mc(s16 temp, u8 resolution) +{ + return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); +} + +/*Get hwmon_dev from i2c_client, set hwmon_dev = NULL is failed.*/ +static struct device * get_hwmon_dev( + struct i2c_client *client) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + if(data) + { + if( data->valid == 1 && data->hwmon_dev) + { + return data->hwmon_dev; + } + + } + return NULL; +} + +/* To find hwmon index by opening hwmon under that i2c address. + */ +static int find_hwmon_index_by_FileOpen( + int bus_nr, + unsigned short addr, + OUT int *index) +{ +#define MAX_HWMON_DEVICE (10) /* Find hwmon device in 0~10*/ + struct file *sfd; + char client_name[96]; + int i=0; + + do { + snprintf(client_name, sizeof(client_name), + "/sys/bus/i2c/devices/%d-%04x/hwmon/hwmon%d/temp1_input", + bus_nr, addr, i); + + sfd = filp_open(client_name, O_RDONLY, 0); + i++; + } while( IS_ERR(sfd) && i < MAX_HWMON_DEVICE); + + if (IS_ERR(sfd)) { + pr_err("Failed to open file(%s)#%d\r\n", client_name, __LINE__); + return -ENOENT; + } + filp_close(sfd, 0); + *index = i - 1; + return 0; + +#undef MAX_HWMON_DEVICE +} + +static int get_temp_file_path( + int bus_nr, unsigned short addr, + struct device *hwmon_dev + ,char *path, int max_len) +{ + + if(hwmon_dev && strlen(dev_name(hwmon_dev))) + { + snprintf(path, max_len, + "/sys/bus/i2c/devices/%d-%04x/hwmon/%s/temp1_input", + bus_nr, addr, dev_name(hwmon_dev)); + } + else + { + int i=0; + if(find_hwmon_index_by_FileOpen( bus_nr, addr, &i)) + { + return -EIO; + } + snprintf(path, max_len, + "/sys/bus/i2c/devices/%d-%04x/hwmon/hwmon%d/temp1_input", + bus_nr, addr, i); + } + return 0; +} + +/*File read the dev file at user space.*/ +static int read_devfile_temp1_input( + struct device *dev, + int bus_nr, + unsigned short addr, + struct device *hwmon_dev, + int *miniCelsius) +{ + struct file *sfd; + char buffer[96]; + char devfile[96]; + int rc, status; + int rdlen, value; + mm_segment_t old_fs; + + rc = 0; + get_temp_file_path(bus_nr, addr, hwmon_dev, devfile, sizeof(devfile)); + sfd = filp_open(devfile, O_RDONLY, 0); + if (IS_ERR(sfd)) { + pr_err("Failed to open file(%s)#%d\r\n", devfile, __LINE__); + return -ENOENT; + } + dev_dbg(dev, "Found device:%s\n",devfile); + + if(!(sfd->f_op) || !(sfd->f_op->read) ) { + pr_err("file %s cann't readable ?\n",devfile); + return -ENOENT; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + rdlen = sfd->f_op->read(sfd, buffer, sizeof(buffer), &sfd->f_pos); + if (rdlen == 0) { + pr_err( "File(%s) empty!\n", devfile); + rc = -EIO; + goto exit; + } + status = sscanf(buffer, "%d", &value); + if (status != 1) { + rc = -EIO; + goto exit; + } + *miniCelsius = value; + dev_dbg(dev,"found sensors: %d @i2c %d-%04x\n", value, bus_nr, addr); + +exit: + set_fs(old_fs); + filp_close(sfd, 0); + return rc; +} + +static u8 is_lm75_data_due(struct i2c_client *client) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + if (time_after(jiffies, data->last_updated + data->sample_time)) + { + return 1; + } + return 0; +} +static int get_lm75_temp(struct i2c_client *client, int *miniCelsius) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + *miniCelsius = lm75_reg_to_mc(data->temp[0], data->resolution); + + return 0; +} + +static bool lm75_addr_mached(unsigned short addr) +{ + int i; + unsigned short addrs[] = THERMAL_SENSORS_ADDRS; + + for (i = 0; i < ARRAY_SIZE(addrs); i++) + { + if( addr == addrs[i]) + return 1; + } + return 0; +} + +static int _find_lm75_device(struct device *dev, void *data) +{ + struct device_driver *driver; + struct as7312_54x_fan_data *prv = data; + char *driver_name = THERMAL_SENSORS_DRIVER; + + driver = dev->driver; + if (driver && driver->name && + strcmp(driver->name, driver_name) == 0) + { + struct i2c_client *client; + client = to_i2c_client(dev); + if (client) + { + /*cannot use "struct i2c_adapter *adap = to_i2c_adapter(dev);"*/ + struct i2c_adapter *adap = client->adapter; + int miniCelsius = 0; + + if (! lm75_addr_mached(client->addr)) + { + return 0; + } + + if (!adap) { + return -ENXIO; + } + + /* If the data is not updated, read them from devfile + to drive them updateing data from chip.*/ + if (is_lm75_data_due(client)) + { + struct device *hwmon_dev; + + hwmon_dev = get_hwmon_dev(client); + if(0 == read_devfile_temp1_input(dev, adap->nr, + client->addr, hwmon_dev, &miniCelsius)) + { + prv->system_temp += miniCelsius; + prv->sensors_found++; + } + + } + else + { + get_lm75_temp(client, &miniCelsius); + prv->system_temp += miniCelsius; + prv->sensors_found++; + + } + } + } + return 0; +} + +/*Find all lm75 devices and return sum of temperatures.*/ +static ssize_t get_sys_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + ssize_t ret = 0; + struct as7312_54x_fan_data *data = as7312_54x_fan_update_device(dev); + + data->system_temp=0; + data->sensors_found=0; + i2c_for_each_dev(data, _find_lm75_device); + if (NUM_THERMAL_SENSORS != data->sensors_found) + { + dev_dbg(dev,"only %d of %d temps are found\n", + data->sensors_found, NUM_THERMAL_SENSORS); + data->system_temp = INT_MAX; + } + ret = sprintf(buf, "%d\n",data->system_temp); + return ret; +} + +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7312_54x_fan_data *data = as7312_54x_fan_update_device(dev); + ssize_t ret = 0; + + if (data->valid) { + switch (attr->index) { + case FAN_DUTY_CYCLE_PERCENTAGE: + { + u32 duty_cycle = reg_val_to_duty_cycle(data->reg_val[FAN_DUTY_CYCLE_PERCENTAGE]); + ret = sprintf(buf, "%u\n", duty_cycle); + break; + } + case FAN1_FRONT_SPEED_RPM: + case FAN2_FRONT_SPEED_RPM: + case FAN3_FRONT_SPEED_RPM: + case FAN4_FRONT_SPEED_RPM: + case FAN5_FRONT_SPEED_RPM: + case FAN6_FRONT_SPEED_RPM: + case FAN1_REAR_SPEED_RPM: + case FAN2_REAR_SPEED_RPM: + case FAN3_REAR_SPEED_RPM: + case FAN4_REAR_SPEED_RPM: + case FAN5_REAR_SPEED_RPM: + case FAN6_REAR_SPEED_RPM: + ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index])); + break; + case FAN1_PRESENT: + case FAN2_PRESENT: + case FAN3_PRESENT: + case FAN4_PRESENT: + case FAN5_PRESENT: + case FAN6_PRESENT: + ret = sprintf(buf, "%d\n", + reg_val_to_is_present(data->reg_val[FAN_PRESENT_REG], + attr->index - FAN1_PRESENT)); + break; + case FAN1_FAULT: + case FAN2_FAULT: + case FAN3_FAULT: + case FAN4_FAULT: + case FAN5_FAULT: + case FAN6_FAULT: + ret = sprintf(buf, "%d\n", is_fan_fault(data, attr->index - FAN1_FAULT)); + break; + case FAN1_DIRECTION: + case FAN2_DIRECTION: + case FAN3_DIRECTION: + case FAN4_DIRECTION: + case FAN5_DIRECTION: + case FAN6_DIRECTION: + ret = sprintf(buf, "%d\n", + reg_val_to_direction(data->reg_val[FAN_DIRECTION_REG], + attr->index - FAN1_DIRECTION)); + break; + default: + break; + } + } + + return ret; +} + +static const struct attribute_group as7312_54x_fan_group = { + .attrs = as7312_54x_fan_attributes, +}; + +static struct as7312_54x_fan_data *as7312_54x_fan_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7312_54x_fan_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || + !data->valid) { + int i; + + dev_dbg(&client->dev, "Starting as7312_54x_fan update\n"); + data->valid = 0; + + /* Update fan data + */ + for (i = 0; i < ARRAY_SIZE(data->reg_val); i++) { + int status = as7312_54x_fan_read_value(client, fan_reg[i]); + + if (status < 0) { + data->valid = 0; + mutex_unlock(&data->update_lock); + dev_dbg(&client->dev, "reg %d, err %d\n", fan_reg[i], status); + return data; + } + else { + data->reg_val[i] = status; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int as7312_54x_fan_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7312_54x_fan_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7312_54x_fan_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->enable = 0; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7312_54x_fan_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: fan '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7312_54x_fan_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7312_54x_fan_remove(struct i2c_client *client) +{ + struct as7312_54x_fan_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7312_54x_fan_group); + + return 0; +} + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x66, I2C_CLIENT_END }; + +static const struct i2c_device_id as7312_54x_fan_id[] = { + { "as7312_54x_fan", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7312_54x_fan_id); + +static struct i2c_driver as7312_54x_fan_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .probe = as7312_54x_fan_probe, + .remove = as7312_54x_fan_remove, + .id_table = as7312_54x_fan_id, + .address_list = normal_i2c, +}; + +static int __init as7312_54x_fan_init(void) +{ + return i2c_add_driver(&as7312_54x_fan_driver); +} + +static void __exit as7312_54x_fan_exit(void) +{ + i2c_del_driver(&as7312_54x_fan_driver); +} + +module_init(as7312_54x_fan_init); +module_exit(as7312_54x_fan_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7312_54x_fan driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_as7312_54x_leds.c b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_as7312_54x_leds.c new file mode 100644 index 000000000000..1d54517c6243 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_as7312_54x_leds.c @@ -0,0 +1,438 @@ +/* + * A LED driver for the accton_as7312_54x_led + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * 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. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern int as7312_54x_cpld_read (unsigned short cpld_addr, u8 reg); +extern int as7312_54x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +extern void led_classdev_unregister(struct led_classdev *led_cdev); +extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev); +extern void led_classdev_resume(struct led_classdev *led_cdev); +extern void led_classdev_suspend(struct led_classdev *led_cdev); + +#define DRVNAME "accton_as7312_54x_led" + +struct accton_as7312_54x_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[1]; /* only 1 register*/ +}; + +static struct accton_as7312_54x_led_data *ledctl = NULL; + +/* LED related data + */ + +#define LED_CNTRLER_I2C_ADDRESS (0x60) + +#define LED_TYPE_DIAG_REG_MASK (0x3) +#define LED_MODE_DIAG_GREEN_VALUE (0x02) +#define LED_MODE_DIAG_RED_VALUE (0x01) +#define LED_MODE_DIAG_AMBER_VALUE (0x00) /*It's yellow actually. Green+Red=Yellow*/ +#define LED_MODE_DIAG_OFF_VALUE (0x03) + + +#define LED_TYPE_LOC_REG_MASK (0x80) +#define LED_MODE_LOC_ON_VALUE (0) +#define LED_MODE_LOC_OFF_VALUE (0x80) + +enum led_type { + LED_TYPE_DIAG, + LED_TYPE_LOC, + LED_TYPE_FAN, + LED_TYPE_PSU1, + LED_TYPE_PSU2 +}; + +struct led_reg { + u32 types; + u8 reg_addr; +}; + +static const struct led_reg led_reg_map[] = { + {(1<update_lock); + + if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) + || !ledctl->valid) { + int i; + + dev_dbg(&ledctl->pdev->dev, "Starting accton_as7312_54x_led update\n"); + + /* Update LED data + */ + for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { + int status = accton_as7312_54x_led_read_value(led_reg_map[i].reg_addr); + + if (status < 0) { + ledctl->valid = 0; + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg_map[i].reg_addr, status); + goto exit; + } + else + { + ledctl->reg_val[i] = status; + } + } + + ledctl->last_updated = jiffies; + ledctl->valid = 1; + } + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void accton_as7312_54x_led_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode, + enum led_type type) +{ + int reg_val; + u8 reg ; + mutex_lock(&ledctl->update_lock); + + if( !accton_getLedReg(type, ®)) + { + dev_dbg(&ledctl->pdev->dev, "Not match item for %d.\n", type); + } + + reg_val = accton_as7312_54x_led_read_value(reg); + + if (reg_val < 0) { + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", reg, reg_val); + goto exit; + } + reg_val = led_light_mode_to_reg_val(type, led_light_mode, reg_val); + accton_as7312_54x_led_write_value(reg, reg_val); + + /* to prevent the slow-update issue */ + ledctl->valid = 0; + +exit: + mutex_unlock(&ledctl->update_lock); +} + + +static void accton_as7312_54x_led_diag_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as7312_54x_led_set(led_cdev, led_light_mode, LED_TYPE_DIAG); +} + +static enum led_brightness accton_as7312_54x_led_diag_get(struct led_classdev *cdev) +{ + accton_as7312_54x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); +} + +static void accton_as7312_54x_led_loc_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as7312_54x_led_set(led_cdev, led_light_mode, LED_TYPE_LOC); +} + +static enum led_brightness accton_as7312_54x_led_loc_get(struct led_classdev *cdev) +{ + accton_as7312_54x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); +} + +static void accton_as7312_54x_led_auto_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ +} + +static enum led_brightness accton_as7312_54x_led_auto_get(struct led_classdev *cdev) +{ + return LED_MODE_AUTO; +} + +static struct led_classdev accton_as7312_54x_leds[] = { + [LED_TYPE_DIAG] = { + .name = "accton_as7312_54x_led::diag", + .default_trigger = "unused", + .brightness_set = accton_as7312_54x_led_diag_set, + .brightness_get = accton_as7312_54x_led_diag_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_RED, + }, + [LED_TYPE_LOC] = { + .name = "accton_as7312_54x_led::loc", + .default_trigger = "unused", + .brightness_set = accton_as7312_54x_led_loc_set, + .brightness_get = accton_as7312_54x_led_loc_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_BLUE, + }, + [LED_TYPE_FAN] = { + .name = "accton_as7312_54x_led::fan", + .default_trigger = "unused", + .brightness_set = accton_as7312_54x_led_auto_set, + .brightness_get = accton_as7312_54x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU1] = { + .name = "accton_as7312_54x_led::psu1", + .default_trigger = "unused", + .brightness_set = accton_as7312_54x_led_auto_set, + .brightness_get = accton_as7312_54x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU2] = { + .name = "accton_as7312_54x_led::psu2", + .default_trigger = "unused", + .brightness_set = accton_as7312_54x_led_auto_set, + .brightness_get = accton_as7312_54x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, +}; + +static int accton_as7312_54x_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as7312_54x_leds); i++) { + led_classdev_suspend(&accton_as7312_54x_leds[i]); + } + + return 0; +} + +static int accton_as7312_54x_led_resume(struct platform_device *dev) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as7312_54x_leds); i++) { + led_classdev_resume(&accton_as7312_54x_leds[i]); + } + + return 0; +} + +static int accton_as7312_54x_led_probe(struct platform_device *pdev) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(accton_as7312_54x_leds); i++) { + ret = led_classdev_register(&pdev->dev, &accton_as7312_54x_leds[i]); + + if (ret < 0) + break; + } + + /* Check if all LEDs were successfully registered */ + if (i != ARRAY_SIZE(accton_as7312_54x_leds)) { + int j; + + /* only unregister the LEDs that were successfully registered */ + for (j = 0; j < i; j++) { + led_classdev_unregister(&accton_as7312_54x_leds[i]); + } + } + + return ret; +} + +static int accton_as7312_54x_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(accton_as7312_54x_leds); i++) { + led_classdev_unregister(&accton_as7312_54x_leds[i]); + } + + return 0; +} + +static struct platform_driver accton_as7312_54x_led_driver = { + .probe = accton_as7312_54x_led_probe, + .remove = accton_as7312_54x_led_remove, + .suspend = accton_as7312_54x_led_suspend, + .resume = accton_as7312_54x_led_resume, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init accton_as7312_54x_led_init(void) +{ + int ret; + + ret = platform_driver_register(&accton_as7312_54x_led_driver); + if (ret < 0) { + goto exit; + } + + ledctl = kzalloc(sizeof(struct accton_as7312_54x_led_data), GFP_KERNEL); + if (!ledctl) { + ret = -ENOMEM; + platform_driver_unregister(&accton_as7312_54x_led_driver); + goto exit; + } + + mutex_init(&ledctl->update_lock); + + ledctl->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(ledctl->pdev)) { + ret = PTR_ERR(ledctl->pdev); + platform_driver_unregister(&accton_as7312_54x_led_driver); + kfree(ledctl); + goto exit; + } + +exit: + return ret; +} + +static void __exit accton_as7312_54x_led_exit(void) +{ + platform_device_unregister(ledctl->pdev); + platform_driver_unregister(&accton_as7312_54x_led_driver); + kfree(ledctl); +} + +module_init(accton_as7312_54x_led_init); +module_exit(accton_as7312_54x_led_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_as7312_54x_led driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_as7312_54x_psu.c b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_as7312_54x_psu.c new file mode 100644 index 000000000000..4646224ef903 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_as7312_54x_psu.c @@ -0,0 +1,277 @@ +/* + * An hwmon driver for accton as7312_54x Power Module + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, char *buf); +static int as7312_54x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); +extern int as7312_54x_cpld_read(unsigned short cpld_addr, u8 reg); + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x50, 0x53, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as7312_54x_psu_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + u8 status; /* Status(present/power_good) register read from CPLD */ + char model_name[9]; /* Model name, read from eeprom */ +}; + +static struct as7312_54x_psu_data *as7312_54x_psu_update_device(struct device *dev); + +enum as7312_54x_psu_sysfs_attributes { + PSU_PRESENT, + PSU_MODEL_NAME, + PSU_POWER_GOOD +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_status, NULL, PSU_PRESENT); +static SENSOR_DEVICE_ATTR(psu_model_name, S_IRUGO, show_model_name,NULL, PSU_MODEL_NAME); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD); + +static struct attribute *as7312_54x_psu_attributes[] = { + &sensor_dev_attr_psu_present.dev_attr.attr, + &sensor_dev_attr_psu_model_name.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + NULL +}; + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7312_54x_psu_data *data = as7312_54x_psu_update_device(dev); + u8 status = 0; + + if (attr->index == PSU_PRESENT) { + status = !(data->status >> (1-data->index) & 0x1); + } + else { /* PSU_POWER_GOOD */ + status = (data->status >> (3-data->index) & 0x1); + } + + return sprintf(buf, "%d\n", status); +} + +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as7312_54x_psu_data *data = as7312_54x_psu_update_device(dev); + + return sprintf(buf, "%s\n", data->model_name); +} + +static const struct attribute_group as7312_54x_psu_group = { + .attrs = as7312_54x_psu_attributes, +}; + +static int as7312_54x_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7312_54x_psu_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7312_54x_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7312_54x_psu_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7312_54x_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7312_54x_psu_remove(struct i2c_client *client) +{ + struct as7312_54x_psu_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7312_54x_psu_group); + kfree(data); + + return 0; +} + +enum psu_index +{ + as7312_54x_psu1, + as7312_54x_psu2 +}; + +static const struct i2c_device_id as7312_54x_psu_id[] = { + { "as7312_54x_psu1", as7312_54x_psu1 }, + { "as7312_54x_psu2", as7312_54x_psu2 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7312_54x_psu_id); + +static struct i2c_driver as7312_54x_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7312_54x_psu", + }, + .probe = as7312_54x_psu_probe, + .remove = as7312_54x_psu_remove, + .id_table = as7312_54x_psu_id, + .address_list = normal_i2c, +}; + +static int as7312_54x_psu_read_block(struct i2c_client *client, u8 command, u8 *data, + int data_len) +{ + int result = 0; + int retry_count = 5; + + while (retry_count) { + retry_count--; + + result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); + + if (unlikely(result < 0)) { + msleep(10); + continue; + } + + if (unlikely(result != data_len)) { + result = -EIO; + msleep(10); + continue; + } + + result = 0; + break; + } + + return result; +} + +static struct as7312_54x_psu_data *as7312_54x_psu_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7312_54x_psu_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int status; + int power_good = 0; + + dev_dbg(&client->dev, "Starting as7312_54x update\n"); + + /* Read psu status */ + status = as7312_54x_cpld_read(0x60, 0x2); + + if (status < 0) { + dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status); + } + else { + data->status = status; + } + + /* Read model name */ + memset(data->model_name, 0, sizeof(data->model_name)); + power_good = (data->status >> (3-data->index) & 0x1); + + if (power_good) { + status = as7312_54x_psu_read_block(client, 0x20, data->model_name, + ARRAY_SIZE(data->model_name)-1); + + if (status < 0) { + data->model_name[0] = '\0'; + dev_dbg(&client->dev, "unable to read model name from (0x%x)\n", client->addr); + } + else { + data->model_name[ARRAY_SIZE(data->model_name)-1] = '\0'; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +module_i2c_driver(as7312_54x_psu_driver); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7312_54x_psu driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_as7312_54x_sfp.c b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_as7312_54x_sfp.c new file mode 100644 index 000000000000..921d9f892cac --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_as7312_54x_sfp.c @@ -0,0 +1,1972 @@ +/* + * SFP driver for accton as7312_54x sfp + * + * Copyright (C) Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "as7312_54x_sfp" /* Platform dependent */ + +#define DEBUG_MODE 0 + +#if (DEBUG_MODE == 1) +#define DEBUG_PRINT(fmt, args...) \ + printk (KERN_INFO "%s:%s[%d]: " fmt "\r\n", __FILE__, __FUNCTION__, __LINE__, ##args) +#else +#define DEBUG_PRINT(fmt, args...) +#endif + +#define NUM_OF_PORT 54 +#define SFP_PORT_MAX 48 +#define EEPROM_NAME "sfp_eeprom" +#define EEPROM_SIZE 256 /* 256 byte eeprom */ +#define BIT_INDEX(i) (1ULL << (i)) +#define USE_I2C_BLOCK_READ 1 /* Platform dependent */ +#define I2C_RW_RETRY_COUNT 10 +#define I2C_RW_RETRY_INTERVAL 60 /* ms */ + +#define SFP_EEPROM_A0_I2C_ADDR (0xA0 >> 1) + +#define SFF8024_PHYSICAL_DEVICE_ID_ADDR 0x0 +#define SFF8024_DEVICE_ID_SFP 0x3 +#define SFF8024_DEVICE_ID_QSFP 0xC +#define SFF8024_DEVICE_ID_QSFP_PLUS 0xD +#define SFF8024_DEVICE_ID_QSFP28 0x11 + +#define SFF8472_DIAG_MON_TYPE_ADDR 92 +#define SFF8472_DIAG_MON_TYPE_DDM_MASK 0x40 +#define SFF8436_RX_LOS_ADDR 3 +#define SFF8436_TX_FAULT_ADDR 4 +#define SFF8436_TX_DISABLE_ADDR 86 + +#define MULTIPAGE_SUPPORT 1 + +#if (MULTIPAGE_SUPPORT == 1) +/* fundamental unit of addressing for SFF_8472/SFF_8436 */ +#define SFF_8436_PAGE_SIZE 128 +/* + * The current 8436 (QSFP) spec provides for only 4 supported + * pages (pages 0-3). + * This driver is prepared to support more, but needs a register in the + * EEPROM to indicate how many pages are supported before it is safe + * to implement more pages in the driver. + */ +#define SFF_8436_SPECED_PAGES 4 +#define SFF_8436_EEPROM_SIZE ((1 + SFF_8436_SPECED_PAGES) * SFF_8436_PAGE_SIZE) +#define SFF_8436_EEPROM_UNPAGED_SIZE (2 * SFF_8436_PAGE_SIZE) +/* + * The current 8472 (SFP) spec provides for only 3 supported + * pages (pages 0-2). + * This driver is prepared to support more, but needs a register in the + * EEPROM to indicate how many pages are supported before it is safe + * to implement more pages in the driver. + */ +#define SFF_8472_SPECED_PAGES 3 +#define SFF_8472_EEPROM_SIZE ((3 + SFF_8472_SPECED_PAGES) * SFF_8436_PAGE_SIZE) +#define SFF_8472_EEPROM_UNPAGED_SIZE (4 * SFF_8436_PAGE_SIZE) + +/* a few constants to find our way around the EEPROM */ +#define SFF_8436_PAGE_SELECT_REG 0x7F +#define SFF_8436_PAGEABLE_REG 0x02 +#define SFF_8436_NOT_PAGEABLE (1<<2) +#define SFF_8472_PAGEABLE_REG 0x40 +#define SFF_8472_PAGEABLE (1<<4) + +/* + * 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 = SFF_8436_PAGE_SIZE; + +/* + * 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; + +typedef enum qsfp_opcode { + QSFP_READ_OP = 0, + QSFP_WRITE_OP = 1 +} qsfp_opcode_e; +#endif + +/* Platform dependent +++ */ +#define I2C_ADDR_CPLD1 0x60 +#define I2C_ADDR_CPLD2 0x62 +#define I2C_ADDR_CPLD3 0x64 + +#define CPLD3_OFFSET_QSFP_MOD_RST 0x17 +/* Platform dependent --- */ +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_present(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t sfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t qsfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t sfp_set_tx_disable(struct device *dev, struct device_attribute *da, const char *buf, size_t count); +static ssize_t qsfp_set_tx_disable(struct device *dev, struct device_attribute *da, const char *buf, size_t count);; +static ssize_t sfp_eeprom_read(struct i2c_client *, u8, u8 *,int); +static ssize_t sfp_eeprom_write(struct i2c_client *, u8 , const char *,int); +static ssize_t get_mode_reset(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t set_mode_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); +enum sfp_sysfs_attributes { + PRESENT, + PRESENT_ALL, + PORT_NUMBER, + PORT_TYPE, + DDM_IMPLEMENTED, + TX_FAULT, + TX_FAULT1, + TX_FAULT2, + TX_FAULT3, + TX_FAULT4, + TX_DISABLE, + TX_DISABLE1, + TX_DISABLE2, + TX_DISABLE3, + TX_DISABLE4, + RX_LOS, + RX_LOS1, + RX_LOS2, + RX_LOS3, + RX_LOS4, + RX_LOS_ALL, + SFP_MOD_RST +}; + +/* SFP/QSFP common attributes for sysfs */ +static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, PORT_NUMBER); +static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_present, NULL, PRESENT); +static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_present, NULL, PRESENT_ALL); +static SENSOR_DEVICE_ATTR(sfp_rx_los, S_IRUGO, sfp_show_tx_rx_status, NULL, RX_LOS); +static SENSOR_DEVICE_ATTR(sfp_tx_disable, S_IWUSR | S_IRUGO, sfp_show_tx_rx_status, sfp_set_tx_disable, TX_DISABLE); +static SENSOR_DEVICE_ATTR(sfp_tx_fault, S_IRUGO, sfp_show_tx_rx_status, NULL, TX_FAULT); + +/* QSFP attributes for sysfs */ +static SENSOR_DEVICE_ATTR(sfp_rx_los1, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS1); +static SENSOR_DEVICE_ATTR(sfp_rx_los2, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS2); +static SENSOR_DEVICE_ATTR(sfp_rx_los3, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS3); +static SENSOR_DEVICE_ATTR(sfp_rx_los4, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS4); +static SENSOR_DEVICE_ATTR(sfp_tx_disable1, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE1); +static SENSOR_DEVICE_ATTR(sfp_tx_disable2, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE2); +static SENSOR_DEVICE_ATTR(sfp_tx_disable3, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE3); +static SENSOR_DEVICE_ATTR(sfp_tx_disable4, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE4); +static SENSOR_DEVICE_ATTR(sfp_tx_fault1, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT1); +static SENSOR_DEVICE_ATTR(sfp_tx_fault2, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT2); +static SENSOR_DEVICE_ATTR(sfp_tx_fault3, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT3); +static SENSOR_DEVICE_ATTR(sfp_tx_fault4, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT4); +static SENSOR_DEVICE_ATTR(sfp_mod_rst, S_IWUSR | S_IRUGO, get_mode_reset, set_mode_reset, SFP_MOD_RST); + +static struct attribute *qsfp_attributes[] = { + &sensor_dev_attr_sfp_port_number.dev_attr.attr, + &sensor_dev_attr_sfp_is_present.dev_attr.attr, + &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los1.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los2.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los3.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los4.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable1.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable2.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable3.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable4.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault1.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault2.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault3.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault4.dev_attr.attr, + &sensor_dev_attr_sfp_mod_rst.dev_attr.attr, + NULL +}; + +/* SFP msa attributes for sysfs */ +static SENSOR_DEVICE_ATTR(sfp_rx_los_all, S_IRUGO, sfp_show_tx_rx_status, NULL, RX_LOS_ALL); +static struct attribute *sfp_msa_attributes[] = { + &sensor_dev_attr_sfp_port_number.dev_attr.attr, + &sensor_dev_attr_sfp_is_present.dev_attr.attr, + &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los_all.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable.dev_attr.attr, + NULL +}; + +/* Platform dependent +++ */ +#define CPLD_PORT_TO_FRONT_PORT(port) (port+1) + +enum port_numbers { + as7312_54x_port1, as7312_54x_port2, as7312_54x_port3, as7312_54x_port4, + as7312_54x_port5, as7312_54x_port6, as7312_54x_port7, as7312_54x_port8, + as7312_54x_port9, as7312_54x_port10, as7312_54x_port11, as7312_54x_port12, + as7312_54x_port13, as7312_54x_port14, as7312_54x_port15, as7312_54x_port16, + as7312_54x_port17, as7312_54x_port18, as7312_54x_port19, as7312_54x_port20, + as7312_54x_port21, as7312_54x_port22, as7312_54x_port23, as7312_54x_port24, + as7312_54x_port25, as7312_54x_port26, as7312_54x_port27, as7312_54x_port28, + as7312_54x_port29, as7312_54x_port30, as7312_54x_port31, as7312_54x_port32, + as7312_54x_port33, as7312_54x_port34, as7312_54x_port35, as7312_54x_port36, + as7312_54x_port37, as7312_54x_port38, as7312_54x_port39, as7312_54x_port40, + as7312_54x_port41, as7312_54x_port42, as7312_54x_port43, as7312_54x_port44, + as7312_54x_port45, as7312_54x_port46, as7312_54x_port47, as7312_54x_port48, + as7312_54x_port49, as7312_54x_port52, as7312_54x_port50, as7312_54x_port53, + as7312_54x_port51, as7312_54x_port54 +}; + +#define I2C_DEV_ID(x) { #x, x} + +static const struct i2c_device_id sfp_device_id[] = { + I2C_DEV_ID(as7312_54x_port1), + I2C_DEV_ID(as7312_54x_port2), + I2C_DEV_ID(as7312_54x_port3), + I2C_DEV_ID(as7312_54x_port4), + I2C_DEV_ID(as7312_54x_port5), + I2C_DEV_ID(as7312_54x_port6), + I2C_DEV_ID(as7312_54x_port7), + I2C_DEV_ID(as7312_54x_port8), + I2C_DEV_ID(as7312_54x_port9), + I2C_DEV_ID(as7312_54x_port10), + I2C_DEV_ID(as7312_54x_port11), + I2C_DEV_ID(as7312_54x_port12), + I2C_DEV_ID(as7312_54x_port13), + I2C_DEV_ID(as7312_54x_port14), + I2C_DEV_ID(as7312_54x_port15), + I2C_DEV_ID(as7312_54x_port16), + I2C_DEV_ID(as7312_54x_port17), + I2C_DEV_ID(as7312_54x_port18), + I2C_DEV_ID(as7312_54x_port19), + I2C_DEV_ID(as7312_54x_port20), + I2C_DEV_ID(as7312_54x_port21), + I2C_DEV_ID(as7312_54x_port22), + I2C_DEV_ID(as7312_54x_port23), + I2C_DEV_ID(as7312_54x_port24), + I2C_DEV_ID(as7312_54x_port25), + I2C_DEV_ID(as7312_54x_port26), + I2C_DEV_ID(as7312_54x_port27), + I2C_DEV_ID(as7312_54x_port28), + I2C_DEV_ID(as7312_54x_port29), + I2C_DEV_ID(as7312_54x_port30), + I2C_DEV_ID(as7312_54x_port31), + I2C_DEV_ID(as7312_54x_port32), + I2C_DEV_ID(as7312_54x_port33), + I2C_DEV_ID(as7312_54x_port34), + I2C_DEV_ID(as7312_54x_port35), + I2C_DEV_ID(as7312_54x_port36), + I2C_DEV_ID(as7312_54x_port37), + I2C_DEV_ID(as7312_54x_port38), + I2C_DEV_ID(as7312_54x_port39), + I2C_DEV_ID(as7312_54x_port40), + I2C_DEV_ID(as7312_54x_port41), + I2C_DEV_ID(as7312_54x_port42), + I2C_DEV_ID(as7312_54x_port43), + I2C_DEV_ID(as7312_54x_port44), + I2C_DEV_ID(as7312_54x_port45), + I2C_DEV_ID(as7312_54x_port46), + I2C_DEV_ID(as7312_54x_port47), + I2C_DEV_ID(as7312_54x_port48), + I2C_DEV_ID(as7312_54x_port49), + I2C_DEV_ID(as7312_54x_port50), + I2C_DEV_ID(as7312_54x_port51), + I2C_DEV_ID(as7312_54x_port52), + I2C_DEV_ID(as7312_54x_port53), + I2C_DEV_ID(as7312_54x_port54), + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, sfp_device_id); +/* Platform dependent --- */ + +enum driver_type_e { + DRIVER_TYPE_SFP_MSA, + DRIVER_TYPE_QSFP +}; + +/* Each client has this additional data + */ +struct eeprom_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + struct bin_attribute bin; /* eeprom data */ +}; + +struct sfp_msa_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u64 status[6]; /* bit0:port0, bit1:port1 and so on */ + /* index 0 => tx_fail + 1 => tx_disable + 2 => rx_loss + 3 => device id + 4 => 10G Ethernet Compliance Codes + to distinguish SFP or SFP+ + 5 => DIAGNOSTIC MONITORING TYPE */ + struct eeprom_data eeprom; +#if (MULTIPAGE_SUPPORT == 1) + struct i2c_client *ddm_client; /* dummy client instance for 0xA2 */ +#endif +}; + +struct qsfp_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 status[3]; /* bit0:port0, bit1:port1 and so on */ + /* index 0 => tx_fail + 1 => tx_disable + 2 => rx_loss */ + + u8 device_id; + struct eeprom_data eeprom; +}; + +struct sfp_port_data { + struct mutex update_lock; + enum driver_type_e driver_type; + int port; /* CPLD port index */ + u64 present; /* present status, bit0:port0, bit1:port1 and so on */ + + struct sfp_msa_data *msa; + struct qsfp_data *qsfp; + + struct i2c_client *client; +#if (MULTIPAGE_SUPPORT == 1) + int use_smbus; + u8 *writebuf; + unsigned write_max; +#endif +}; + +#if (MULTIPAGE_SUPPORT == 1) +static ssize_t sfp_port_read_write(struct sfp_port_data *port_data, + char *buf, loff_t off, size_t len, qsfp_opcode_e opcode); +#endif +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + return sprintf(buf, "%d\n", CPLD_PORT_TO_FRONT_PORT(data->port)); +} + +/* Platform dependent +++ */ +static struct sfp_port_data *sfp_update_present(struct i2c_client *client) +{ + int i = 0, j = 0, status = -1; + u8 reg; + unsigned short cpld_addr; + struct sfp_port_data *data = i2c_get_clientdata(client); + + DEBUG_PRINT("Starting sfp present status update"); + mutex_lock(&data->update_lock); + data->present = 0; + + /* Read present status of port 1~48(SFP port) */ + for (i = 0; i < 2; i++) { + for (j = 0; j < 3; j++) { + cpld_addr = I2C_ADDR_CPLD2 + i*2; + reg = 0x9+j; + status = accton_i2c_cpld_read(cpld_addr, reg); + + if (unlikely(status < 0)) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", cpld_addr, reg, status); + goto exit; + } + + DEBUG_PRINT("Present status = 0x%lx\r\n", data->present); + data->present |= (u64)status << ((i*24) + (j%3)*8); + } + } + + /* Read present status of port 49-52(QSFP port) */ + cpld_addr = I2C_ADDR_CPLD2; + reg = 0x18; + status = accton_i2c_cpld_read(cpld_addr, reg); + + if (unlikely(status < 0)) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", cpld_addr, reg, status); + goto exit; + } + else { + data->present |= (u64)(status & 0xF) << SFP_PORT_MAX; + } + + /* Read present status of port 53-54(QSFP port) */ + cpld_addr = I2C_ADDR_CPLD3; + reg = 0x18; + status = accton_i2c_cpld_read(cpld_addr, reg); + + if (unlikely(status < 0)) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", cpld_addr, reg, status); + goto exit; + } + else { + data->present |= (u64)(status & 0x3) << 52; + } + + DEBUG_PRINT("Present status = 0x%lx", data->present); +exit: + mutex_unlock(&data->update_lock); + return (status < 0) ? ERR_PTR(status) : data; +} + +static struct sfp_port_data* sfp_update_tx_rx_status(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + int i = 0, j = 0; + int status = -1; + + if (time_before(jiffies, data->msa->last_updated + HZ + HZ / 2) && data->msa->valid) { + return data; + } + + DEBUG_PRINT("Starting as7312_54x sfp tx rx status update"); + mutex_lock(&data->update_lock); + data->msa->valid = 0; + memset(data->msa->status, 0, sizeof(data->msa->status)); + + /* Read status of port 1~48(SFP port) */ + for (i = 0; i < 2; i++) { + for (j = 0; j < 9; j++) { + u8 reg; + unsigned short cpld_addr; + reg = 0xc+j; + cpld_addr = I2C_ADDR_CPLD2 + i*2; + + status = accton_i2c_cpld_read(cpld_addr, reg); + if (unlikely(status < 0)) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", cpld_addr, reg, status); + goto exit; + } + + data->msa->status[j/3] |= (u64)status << ((i*24) + (j%3)*8); + } + } + + data->msa->valid = 1; + data->msa->last_updated = jiffies; + +exit: + mutex_unlock(&data->update_lock); + return (status < 0) ? ERR_PTR(status) : data; +} + +static ssize_t sfp_set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + unsigned short cpld_addr = 0; + u8 cpld_reg = 0, cpld_val = 0, cpld_bit = 0; + long disable; + int error; + + if (data->driver_type == DRIVER_TYPE_QSFP) { + return qsfp_set_tx_disable(dev, da, buf, count); + } + + error = kstrtol(buf, 10, &disable); + if (error) { + return error; + } + + mutex_lock(&data->update_lock); + + if(data->port < 24) { + cpld_addr = I2C_ADDR_CPLD2; + cpld_reg = 0xF + data->port / 8; + cpld_bit = 1 << (data->port % 8); + } + else { /* port 24 ~ 48 */ + cpld_addr = I2C_ADDR_CPLD3; + cpld_reg = 0xF + (data->port - 24) / 8; + cpld_bit = 1 << (data->port % 8); + } + + /* Read current status */ + cpld_val = accton_i2c_cpld_read(cpld_addr, cpld_reg); + + /* Update tx_disable status */ + if (disable) { + data->msa->status[1] |= BIT_INDEX(data->port); + cpld_val |= cpld_bit; + } + else { + data->msa->status[1] &= ~BIT_INDEX(data->port); + cpld_val &= ~cpld_bit; + } + + accton_i2c_cpld_write(cpld_addr, cpld_reg, cpld_val); + mutex_unlock(&data->update_lock); + return count; +} + +static int sfp_is_port_present(struct i2c_client *client, int port) +{ + struct sfp_port_data *data = i2c_get_clientdata(client); + + data = sfp_update_present(client); + if (IS_ERR(data)) { + return PTR_ERR(data); + } + + return (data->present & BIT_INDEX(data->port)) ? 0 : 1; /* Platform dependent */ +} + +/* Platform dependent +++ */ +static ssize_t show_present(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + + if (PRESENT_ALL == attr->index) { + int i; + u8 values[7] = {0}; + struct sfp_port_data *data = sfp_update_present(client); + + if (IS_ERR(data)) { + return PTR_ERR(data); + } + + for (i = 0; i < ARRAY_SIZE(values); i++) { + values[i] = ~(u8)(data->present >> (i * 8)); + } + + /* Return values 1 -> 54 in order */ + return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], + values[3], values[4], values[5], + values[6] & 0x3F); + } + else { + struct sfp_port_data *data = i2c_get_clientdata(client); + int present = sfp_is_port_present(client, data->port); + + if (IS_ERR_VALUE(present)) { + return present; + } + + /* PRESENT */ + return sprintf(buf, "%d\n", present); + } +} +/* Platform dependent --- */ + +static struct sfp_port_data *qsfp_update_tx_rx_status(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + int i, status = -1; + u8 buf = 0; + u8 reg[] = {SFF8436_TX_FAULT_ADDR, SFF8436_TX_DISABLE_ADDR, SFF8436_RX_LOS_ADDR}; + + if (time_before(jiffies, data->qsfp->last_updated + HZ + HZ / 2) && data->qsfp->valid) { + return data; + } + + DEBUG_PRINT("Starting sfp tx rx status update"); + mutex_lock(&data->update_lock); + data->qsfp->valid = 0; + memset(data->qsfp->status, 0, sizeof(data->qsfp->status)); + + /* Notify device to update tx fault/ tx disable/ rx los status */ + for (i = 0; i < ARRAY_SIZE(reg); i++) { + status = sfp_eeprom_read(client, reg[i], &buf, sizeof(buf)); + if (unlikely(status < 0)) { + goto exit; + } + } + msleep(200); + + /* Read actual tx fault/ tx disable/ rx los status */ + for (i = 0; i < ARRAY_SIZE(reg); i++) { + status = sfp_eeprom_read(client, reg[i], &buf, sizeof(buf)); + if (unlikely(status < 0)) { + goto exit; + } + + DEBUG_PRINT("qsfp reg(0x%x) status = (0x%x)", reg[i], data->qsfp->status[i]); + data->qsfp->status[i] = (buf & 0xF); + } + + data->qsfp->valid = 1; + data->qsfp->last_updated = jiffies; + +exit: + mutex_unlock(&data->update_lock); + return (status < 0) ? ERR_PTR(status) : data; +} + +static ssize_t get_mode_reset(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + u8 cpld_val = 0; + int port_bit; + int status = -EINVAL; + u8 cpld_addr[] = {I2C_ADDR_CPLD2, I2C_ADDR_CPLD3}; + + /* Low power mode is not supported for SFP ports(1-48) */ + if (data->port < SFP_PORT_MAX) { + return -EINVAL; + } + mutex_lock(&data->update_lock); + + port_bit = data->port - SFP_PORT_MAX; + cpld_val = accton_i2c_cpld_read(cpld_addr[port_bit/4], CPLD3_OFFSET_QSFP_MOD_RST); + + pr_err("[ROY]%s#%d, %x from %x\n", __func__, __LINE__, cpld_val, cpld_addr[port_bit/4]); + + cpld_val = cpld_val & 0x0F; + cpld_val = cpld_val & BIT_INDEX(port_bit%4); + + pr_err("[ROY]%s#%d, %x of bit %d\n", __func__, __LINE__, cpld_val, port_bit); + + status = snprintf(buf, PAGE_SIZE - 1, "%d\r\n", cpld_val>>(port_bit%4)); + + mutex_unlock(&data->update_lock); + + return status; +} + +static ssize_t set_mode_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + u8 cpld_val = 0; + long reset; + int error, port_bit; + u8 cpld_addr[] = {I2C_ADDR_CPLD2, I2C_ADDR_CPLD3}; + + pr_err("[ROY]%s#%d, port:%d\n", __func__, __LINE__, data->port); + + /* Tx disable is not supported for QSFP ports(49-54) */ + if (data->port < SFP_PORT_MAX) { + return -EINVAL; + } + port_bit = data->port - SFP_PORT_MAX; + error = kstrtol(buf, 10, &reset); + + pr_err("[ROY]%s#%d, %s == %d\n", __func__, __LINE__, buf, error); + if (error) { + return error; + } + mutex_lock(&data->update_lock); + + cpld_val = accton_i2c_cpld_read(cpld_addr[port_bit/4], CPLD3_OFFSET_QSFP_MOD_RST); + pr_err("[ROY]%s#%d, %x\n", __func__, __LINE__, cpld_val); + /* Update lp_mode status */ + if (reset) + { + cpld_val |= BIT_INDEX(port_bit%4); + } + else + { + cpld_val &= ~BIT_INDEX(port_bit%4); + } + pr_err("[ROY]%s#%d, %x to %x\n", __func__, __LINE__, cpld_val, cpld_addr[port_bit/4]); + + accton_i2c_cpld_write(cpld_addr[port_bit/4], CPLD3_OFFSET_QSFP_MOD_RST, cpld_val); + + mutex_unlock(&data->update_lock); + + return count; +} +static ssize_t qsfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + int present; + u8 val = 0; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + + present = sfp_is_port_present(client, data->port); + if (IS_ERR_VALUE(present)) { + return present; + } + + if (present == 0) { + /* port is not present */ + return -ENXIO; + } + + data = qsfp_update_tx_rx_status(dev); + if (IS_ERR(data)) { + return PTR_ERR(data); + } + + switch (attr->index) { + case TX_FAULT: + val = !!(data->qsfp->status[2] & 0xF); + break; + case TX_FAULT1: + case TX_FAULT2: + case TX_FAULT3: + case TX_FAULT4: + val = !!(data->qsfp->status[2] & BIT_INDEX(attr->index - TX_FAULT1)); + break; + case TX_DISABLE: + val = data->qsfp->status[1] & 0xF; + break; + case TX_DISABLE1: + case TX_DISABLE2: + case TX_DISABLE3: + case TX_DISABLE4: + val = !!(data->qsfp->status[1] & BIT_INDEX(attr->index - TX_DISABLE1)); + break; + case RX_LOS: + val = !!(data->qsfp->status[0] & 0xF); + break; + case RX_LOS1: + case RX_LOS2: + case RX_LOS3: + case RX_LOS4: + val = !!(data->qsfp->status[0] & BIT_INDEX(attr->index - RX_LOS1)); + break; + default: + break; + } + + return sprintf(buf, "%d\n", val); +} + +static ssize_t qsfp_set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + long disable; + int status; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + + status = sfp_is_port_present(client, data->port); + if (IS_ERR_VALUE(status)) { + return status; + } + + if (!status) { + /* port is not present */ + return -ENXIO; + } + + status = kstrtol(buf, 10, &disable); + if (status) { + return status; + } + + data = qsfp_update_tx_rx_status(dev); + if (IS_ERR(data)) { + return PTR_ERR(data); + } + + mutex_lock(&data->update_lock); + + if (attr->index == TX_DISABLE) { + if (disable) { + data->qsfp->status[1] |= 0xF; + } + else { + data->qsfp->status[1] &= ~0xF; + } + } + else {/* TX_DISABLE1 ~ TX_DISABLE4*/ + if (disable) { + data->qsfp->status[1] |= (1 << (attr->index - TX_DISABLE1)); + } + else { + data->qsfp->status[1] &= ~(1 << (attr->index - TX_DISABLE1)); + } + } + + DEBUG_PRINT("index = (%d), status = (0x%x)", attr->index, data->qsfp->status[1]); + status = sfp_eeprom_write(data->client, SFF8436_TX_DISABLE_ADDR, &data->qsfp->status[1], sizeof(data->qsfp->status[1])); + if (unlikely(status < 0)) { + count = status; + } + + mutex_unlock(&data->update_lock); + return count; +} + +/* Platform dependent +++ */ +static ssize_t sfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + u8 val = 0, index = 0; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + + if (data->driver_type == DRIVER_TYPE_QSFP) { + return qsfp_show_tx_rx_status(dev, da, buf); + } + + data = sfp_update_tx_rx_status(dev); + if (IS_ERR(data)) { + return PTR_ERR(data); + } + + if(attr->index == RX_LOS_ALL) { + int i = 0; + u8 values[6] = {0}; + + for (i = 0; i < ARRAY_SIZE(values); i++) { + values[i] = (u8)(data->msa->status[2] >> (i * 8)); + } + + /** Return values 1 -> 48 in order */ + return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], + values[3], values[4], values[5]); + } + + switch (attr->index) { + case TX_FAULT: + index = 0; + break; + case TX_DISABLE: + index = 1; + break; + case RX_LOS: + index = 2; + break; + default: + return 0; + } + + val = (data->msa->status[index] & BIT_INDEX(data->port)) ? 1 : 0; + return sprintf(buf, "%d\n", val); +} +/* Platform dependent --- */ +static ssize_t sfp_eeprom_write(struct i2c_client *client, u8 command, const char *data, + int data_len) +{ +#if USE_I2C_BLOCK_READ + int status, retry = I2C_RW_RETRY_COUNT; + + if (data_len > I2C_SMBUS_BLOCK_MAX) { + data_len = I2C_SMBUS_BLOCK_MAX; + } + + while (retry) { + status = i2c_smbus_write_i2c_block_data(client, command, data_len, data); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(status < 0)) { + return status; + } + + return data_len; +#else + int status, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_write_byte_data(client, command, *data); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(status < 0)) { + return status; + } + + return 1; +#endif + + +} + +#if (MULTIPAGE_SUPPORT == 0) +static ssize_t sfp_port_write(struct sfp_port_data *data, + 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(&data->update_lock); + + while (count) { + ssize_t status; + + status = sfp_eeprom_write(data->client, off, buf, count); + if (status <= 0) { + if (retval == 0) { + retval = status; + } + break; + } + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&data->update_lock); + return retval; +} +#endif + +static ssize_t sfp_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + int present; + struct sfp_port_data *data; + DEBUG_PRINT("%s(%d) offset = (%d), count = (%d)", off, count); + data = dev_get_drvdata(container_of(kobj, struct device, kobj)); + + present = sfp_is_port_present(data->client, data->port); + if (IS_ERR_VALUE(present)) { + return present; + } + + if (present == 0) { + /* port is not present */ + return -ENODEV; + } + +#if (MULTIPAGE_SUPPORT == 1) + return sfp_port_read_write(data, buf, off, count, QSFP_WRITE_OP); +#else + return sfp_port_write(data, buf, off, count); +#endif +} + +static ssize_t sfp_eeprom_read(struct i2c_client *client, u8 command, u8 *data, + int data_len) +{ +#if USE_I2C_BLOCK_READ + int status, retry = I2C_RW_RETRY_COUNT; + + if (data_len > I2C_SMBUS_BLOCK_MAX) { + data_len = I2C_SMBUS_BLOCK_MAX; + } + + while (retry) { + status = i2c_smbus_read_i2c_block_data(client, command, data_len, data); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(status < 0)) { + goto abort; + } + if (unlikely(status != data_len)) { + status = -EIO; + goto abort; + } + + //result = data_len; + +abort: + return status; +#else + int status, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, command); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(status < 0)) { + dev_dbg(&client->dev, "sfp read byte data failed, command(0x%2x), data(0x%2x)\r\n", command, status); + goto abort; + } + + *data = (u8)status; + status = 1; + +abort: + return status; +#endif +} + +#if (MULTIPAGE_SUPPORT == 1) +/*-------------------------------------------------------------------------*/ +/* + * This routine computes the addressing information to be used for + * a given r/w request. + * + * Task is to calculate the client (0 = i2c addr 50, 1 = i2c addr 51), + * the page, and the offset. + * + * Handles both SFP and QSFP. + * For SFP, offset 0-255 are on client[0], >255 is on client[1] + * Offset 256-383 are on the lower half of client[1] + * Pages are accessible on the upper half of client[1]. + * Offset >383 are in 128 byte pages mapped into the upper half + * + * For QSFP, all offsets are on client[0] + * offset 0-127 are on the lower half of client[0] (no paging) + * Pages are accessible on the upper half of client[1]. + * Offset >127 are in 128 byte pages mapped into the upper half + * + * Callers must not read/write beyond the end of a client or a page + * without recomputing the client/page. Hence offset (within page) + * plus length must be less than or equal to 128. (Note that this + * routine does not have access to the length of the call, hence + * cannot do the validity check.) + * + * Offset within Lower Page 00h and Upper Page 00h are not recomputed + */ +static uint8_t sff_8436_translate_offset(struct sfp_port_data *port_data, + loff_t *offset, struct i2c_client **client) +{ + unsigned page = 0; + + *client = port_data->client; + + /* if SFP style, offset > 255, shift to i2c addr 0x51 */ + if (port_data->driver_type == DRIVER_TYPE_SFP_MSA) { + if (*offset > 255) { + /* like QSFP, but shifted to client[1] */ + *client = port_data->msa->ddm_client; + *offset -= 256; + } + } + + /* + * if offset is in the range 0-128... + * page doesn't matter (using lower half), return 0. + * offset is already correct (don't add 128 to get to paged area) + */ + if (*offset < SFF_8436_PAGE_SIZE) + return page; + + /* note, page will always be positive since *offset >= 128 */ + page = (*offset >> 7)-1; + /* 0x80 places the offset in the top half, offset is last 7 bits */ + *offset = SFF_8436_PAGE_SIZE + (*offset & 0x7f); + + return page; /* note also returning client and offset */ +} + +static ssize_t sff_8436_eeprom_read(struct sfp_port_data *port_data, + struct i2c_client *client, + char *buf, unsigned offset, size_t count) +{ + struct i2c_msg msg[2]; + u8 msgbuf[2]; + unsigned long timeout, read_time; + int status, i; + + memset(msg, 0, sizeof(msg)); + + switch (port_data->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* + * When we have a better choice than SMBus calls, use a + * combined I2C message. Write address; then read up to + * io_limit data bytes. msgbuf is u8 and will cast to our + * needs. + */ + i = 0; + msgbuf[i++] = offset; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + read_time = jiffies; + + switch (port_data->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_read_i2c_block_data(client, offset, + count, buf); + break; + case I2C_SMBUS_WORD_DATA: + status = i2c_smbus_read_word_data(client, offset); + if (status >= 0) { + buf[0] = status & 0xff; + if (count == 2) + buf[1] = status >> 8; + status = count; + } + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_read_byte_data(client, offset); + if (status >= 0) { + buf[0] = status; + status = count; + } + break; + default: + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + status = count; + } + + dev_dbg(&client->dev, "eeprom read %zu@%d --> %d (%ld)\n", + count, offset, status, jiffies); + + if (status == count) /* happy path */ + return count; + + if (status == -ENXIO) /* no module present */ + return status; + + /* REVISIT: at HZ=100, this is sloooow */ + msleep(1); + } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; +} + +static ssize_t sff_8436_eeprom_write(struct sfp_port_data *port_data, + struct i2c_client *client, + const char *buf, + unsigned offset, size_t count) +{ + struct i2c_msg msg; + ssize_t status; + unsigned long timeout, write_time; + unsigned next_page_start; + int i = 0; + + /* write max is at most a page + * (In this driver, write_max is actually one byte!) + */ + if (count > port_data->write_max) + count = port_data->write_max; + + /* shorten count if necessary to avoid crossing page boundary */ + next_page_start = roundup(offset + 1, SFF_8436_PAGE_SIZE); + if (offset + count > next_page_start) + count = next_page_start - offset; + + switch (port_data->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* If we'll use I2C calls for I/O, set up the message */ + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = port_data->writebuf; + + msg.buf[i++] = offset; + memcpy(&msg.buf[i], buf, count); + msg.len = i + count; + break; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + write_time = jiffies; + + switch (port_data->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_write_i2c_block_data(client, + offset, count, buf); + if (status == 0) + status = count; + break; + case I2C_SMBUS_WORD_DATA: + if (count == 2) { + status = i2c_smbus_write_word_data(client, + offset, (u16)((buf[0])|(buf[1] << 8))); + } else { + /* count = 1 */ + status = i2c_smbus_write_byte_data(client, + offset, buf[0]); + } + if (status == 0) + status = count; + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_write_byte_data(client, offset, + buf[0]); + if (status == 0) + status = count; + break; + default: + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + break; + } + + dev_dbg(&client->dev, "eeprom write %zu@%d --> %ld (%lu)\n", + count, offset, (long int) status, jiffies); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + msleep(1); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + + +static ssize_t sff_8436_eeprom_update_client(struct sfp_port_data *port_data, + char *buf, loff_t off, + size_t count, qsfp_opcode_e opcode) +{ + struct i2c_client *client; + ssize_t retval = 0; + u8 page = 0; + loff_t phy_offset = off; + int ret = 0; + + page = sff_8436_translate_offset(port_data, &phy_offset, &client); + + dev_dbg(&client->dev, + "sff_8436_eeprom_update_client off %lld page:%d phy_offset:%lld, count:%ld, opcode:%d\n", + off, page, phy_offset, (long int) count, opcode); + if (page > 0) { + ret = sff_8436_eeprom_write(port_data, client, &page, + SFF_8436_PAGE_SELECT_REG, 1); + if (ret < 0) { + dev_dbg(&client->dev, + "Write page register for page %d failed ret:%d!\n", + page, ret); + return ret; + } + } + + while (count) { + ssize_t status; + + if (opcode == QSFP_READ_OP) { + status = sff_8436_eeprom_read(port_data, client, + buf, phy_offset, count); + } else { + status = sff_8436_eeprom_write(port_data, client, + buf, phy_offset, count); + } + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + buf += status; + phy_offset += status; + count -= status; + retval += status; + } + + + if (page > 0) { + /* return the page register to page 0 (why?) */ + page = 0; + ret = sff_8436_eeprom_write(port_data, client, &page, + SFF_8436_PAGE_SELECT_REG, 1); + if (ret < 0) { + dev_err(&client->dev, + "Restore page register to page %d failed ret:%d!\n", + page, ret); + return ret; + } + } + return retval; +} + + +/* + * Figure out if this access is within the range of supported pages. + * Note this is called on every access because we don't know if the + * module has been replaced since the last call. + * If/when modules support more pages, this is the routine to update + * to validate and allow access to additional pages. + * + * Returns updated len for this access: + * - entire access is legal, original len is returned. + * - access begins legal but is too long, len is truncated to fit. + * - initial offset exceeds supported pages, return -EINVAL + */ +static ssize_t sff_8436_page_legal(struct sfp_port_data *port_data, + loff_t off, size_t len) +{ + struct i2c_client *client = port_data->client; + u8 regval; + int status; + size_t maxlen; + + if (off < 0) return -EINVAL; + if (port_data->driver_type == DRIVER_TYPE_SFP_MSA) { + /* SFP case */ + if ((off + len) <= 256) return len; + /* if no pages needed, we're good */ + //if ((off + len) <= SFF_8472_EEPROM_UNPAGED_SIZE) return len; + /* if offset exceeds possible pages, we're not good */ + if (off >= SFF_8472_EEPROM_SIZE) return -EINVAL; + + /* Check if ddm is supported */ + status = sff_8436_eeprom_read(port_data, client, ®val, + SFF8472_DIAG_MON_TYPE_ADDR, 1); + if (status < 0) return status; /* error out (no module?) */ + if (!(regval & SFF8472_DIAG_MON_TYPE_DDM_MASK)) { + if (off >= 256) return -EINVAL; + maxlen = 256 - off; + } + else { + /* in between, are pages supported? */ + status = sff_8436_eeprom_read(port_data, client, ®val, + SFF_8472_PAGEABLE_REG, 1); + if (status < 0) return status; /* error out (no module?) */ + if (regval & SFF_8472_PAGEABLE) { + /* Pages supported, trim len to the end of pages */ + maxlen = SFF_8472_EEPROM_SIZE - off; + } else { + /* pages not supported, trim len to unpaged size */ + if (off >= SFF_8472_EEPROM_UNPAGED_SIZE) return -EINVAL; + maxlen = SFF_8472_EEPROM_UNPAGED_SIZE - off; + } + } + len = (len > maxlen) ? maxlen : len; + dev_dbg(&client->dev, + "page_legal, SFP, off %lld len %ld\n", + off, (long int) len); + } + else if (port_data->driver_type == DRIVER_TYPE_QSFP) { + /* QSFP case */ + /* if no pages needed, we're good */ + if ((off + len) <= SFF_8436_EEPROM_UNPAGED_SIZE) return len; + /* if offset exceeds possible pages, we're not good */ + if (off >= SFF_8436_EEPROM_SIZE) return -EINVAL; + /* in between, are pages supported? */ + status = sff_8436_eeprom_read(port_data, client, ®val, + SFF_8436_PAGEABLE_REG, 1); + if (status < 0) return status; /* error out (no module?) */ + if (regval & SFF_8436_NOT_PAGEABLE) { + /* pages not supported, trim len to unpaged size */ + if (off >= SFF_8436_EEPROM_UNPAGED_SIZE) return -EINVAL; + maxlen = SFF_8436_EEPROM_UNPAGED_SIZE - off; + } else { + /* Pages supported, trim len to the end of pages */ + maxlen = SFF_8436_EEPROM_SIZE - off; + } + len = (len > maxlen) ? maxlen : len; + dev_dbg(&client->dev, + "page_legal, QSFP, off %lld len %ld\n", + off, (long int) len); + } + else { + return -EINVAL; + } + return len; +} + + +static ssize_t sfp_port_read_write(struct sfp_port_data *port_data, + char *buf, loff_t off, size_t len, qsfp_opcode_e opcode) +{ + struct i2c_client *client = port_data->client; + int chunk; + int status = 0; + ssize_t retval; + size_t pending_len = 0, chunk_len = 0; + loff_t chunk_offset = 0, chunk_start_offset = 0; + + if (unlikely(!len)) + return len; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&port_data->update_lock); + + /* + * Confirm this access fits within the device suppored addr range + */ + len = sff_8436_page_legal(port_data, off, len); + if (len < 0) { + status = len; + goto err; + } + + /* + * For each (128 byte) chunk involved in this request, issue a + * separate call to sff_eeprom_update_client(), to + * ensure that each access recalculates the client/page + * and writes the page register as needed. + * Note that chunk to page mapping is confusing, is different for + * QSFP and SFP, and never needs to be done. Don't try! + */ + pending_len = len; /* amount remaining to transfer */ + retval = 0; /* amount transferred */ + for (chunk = off >> 7; chunk <= (off + len - 1) >> 7; chunk++) { + + /* + * Compute the offset and number of bytes to be read/write + * + * 1. start at offset 0 (within the chunk), and read/write + * the entire chunk + * 2. start at offset 0 (within the chunk) and read/write less + * than entire chunk + * 3. start at an offset not equal to 0 and read/write the rest + * of the chunk + * 4. start at an offset not equal to 0 and read/write less than + * (end of chunk - offset) + */ + chunk_start_offset = chunk * SFF_8436_PAGE_SIZE; + + if (chunk_start_offset < off) { + chunk_offset = off; + if ((off + pending_len) < (chunk_start_offset + + SFF_8436_PAGE_SIZE)) + chunk_len = pending_len; + else + chunk_len = (chunk+1)*SFF_8436_PAGE_SIZE - off;/*SFF_8436_PAGE_SIZE - off;*/ + } else { + chunk_offset = chunk_start_offset; + if (pending_len > SFF_8436_PAGE_SIZE) + chunk_len = SFF_8436_PAGE_SIZE; + else + chunk_len = pending_len; + } + + dev_dbg(&client->dev, + "sff_r/w: off %lld, len %ld, chunk_start_offset %lld, chunk_offset %lld, chunk_len %ld, pending_len %ld\n", + off, (long int) len, chunk_start_offset, chunk_offset, + (long int) chunk_len, (long int) pending_len); + + /* + * note: chunk_offset is from the start of the EEPROM, + * not the start of the chunk + */ + status = sff_8436_eeprom_update_client(port_data, buf, + chunk_offset, chunk_len, opcode); + if (status != chunk_len) { + /* This is another 'no device present' path */ + dev_dbg(&client->dev, + "sff_8436_update_client for chunk %d chunk_offset %lld chunk_len %ld failed %d!\n", + chunk, chunk_offset, (long int) chunk_len, status); + goto err; + } + buf += status; + pending_len -= status; + retval += status; + } + mutex_unlock(&port_data->update_lock); + + return retval; + +err: + mutex_unlock(&port_data->update_lock); + + return status; +} + +#else +static ssize_t sfp_port_read(struct sfp_port_data *data, + char *buf, loff_t off, size_t count) +{ + ssize_t retval = 0; + + if (unlikely(!count)) { + DEBUG_PRINT("Count = 0, return"); + return count; + } + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&data->update_lock); + + while (count) { + ssize_t status; + + status = sfp_eeprom_read(data->client, off, buf, count); + if (status <= 0) { + if (retval == 0) { + retval = status; + } + break; + } + + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&data->update_lock); + return retval; + +} +#endif + +static ssize_t sfp_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + int present; + struct sfp_port_data *data; + DEBUG_PRINT("offset = (%d), count = (%d)", off, count); + data = dev_get_drvdata(container_of(kobj, struct device, kobj)); + + present = sfp_is_port_present(data->client, data->port); + if (IS_ERR_VALUE(present)) { + return present; + } + + if (present == 0) { + /* port is not present */ + return -ENODEV; + } + +#if (MULTIPAGE_SUPPORT == 1) + return sfp_port_read_write(data, buf, off, count, QSFP_READ_OP); +#else + return sfp_port_read(data, buf, off, count); +#endif +} + +#if (MULTIPAGE_SUPPORT == 1) +static int sfp_sysfs_eeprom_init(struct kobject *kobj, struct bin_attribute *eeprom, size_t size) +#else +static int sfp_sysfs_eeprom_init(struct kobject *kobj, struct bin_attribute *eeprom) +#endif +{ + int err; + + sysfs_bin_attr_init(eeprom); + eeprom->attr.name = EEPROM_NAME; + eeprom->attr.mode = S_IWUSR | S_IRUGO; + eeprom->read = sfp_bin_read; + eeprom->write = sfp_bin_write; +#if (MULTIPAGE_SUPPORT == 1) + eeprom->size = size; +#else + eeprom->size = EEPROM_SIZE; +#endif + + /* Create eeprom file */ + err = sysfs_create_bin_file(kobj, eeprom); + if (err) { + return err; + } + + return 0; +} + +static int sfp_sysfs_eeprom_cleanup(struct kobject *kobj, struct bin_attribute *eeprom) +{ + sysfs_remove_bin_file(kobj, eeprom); + return 0; +} + + +#if (MULTIPAGE_SUPPORT == 0) +static int sfp_i2c_check_functionality(struct i2c_client *client) +{ +#if USE_I2C_BLOCK_READ + return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK); +#else + return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA); +#endif +} +#endif + +static const struct attribute_group sfp_msa_group = { + .attrs = sfp_msa_attributes, +}; + +static int sfp_msa_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, + struct sfp_msa_data **data) +{ + int status; + struct sfp_msa_data *msa; + +#if (MULTIPAGE_SUPPORT == 0) + if (!sfp_i2c_check_functionality(client)) { + status = -EIO; + goto exit; + } +#endif + + msa = kzalloc(sizeof(struct sfp_msa_data), GFP_KERNEL); + if (!msa) { + status = -ENOMEM; + goto exit; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &sfp_msa_group); + if (status) { + goto exit_free; + } + + /* init eeprom */ +#if (MULTIPAGE_SUPPORT == 1) + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &msa->eeprom.bin, SFF_8436_EEPROM_SIZE); +#else + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &msa->eeprom.bin); +#endif + if (status) { + goto exit_remove; + } + +#if (MULTIPAGE_SUPPORT == 1) + msa->ddm_client = i2c_new_dummy(client->adapter, client->addr + 1); + if (!msa->ddm_client) { + dev_err(&client->dev, "address 0x%02x unavailable\n", client->addr + 1); + status = -EADDRINUSE; + goto exit_eeprom; + } +#endif + + *data = msa; + dev_info(&client->dev, "sfp msa '%s'\n", client->name); + + return 0; + +exit_eeprom: + sfp_sysfs_eeprom_cleanup(&client->dev.kobj, &msa->eeprom.bin); +exit_remove: + sysfs_remove_group(&client->dev.kobj, &sfp_msa_group); +exit_free: + kfree(msa); +exit: + + return status; +} + +static const struct attribute_group qsfp_group = { + .attrs = qsfp_attributes, +}; + +static int qsfp_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, + struct qsfp_data **data) +{ + int status; + struct qsfp_data *qsfp; + +#if (MULTIPAGE_SUPPORT == 0) + if (!sfp_i2c_check_functionality(client)) { + status = -EIO; + goto exit; + } +#endif + + qsfp = kzalloc(sizeof(struct qsfp_data), GFP_KERNEL); + if (!qsfp) { + status = -ENOMEM; + goto exit; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &qsfp_group); + if (status) { + goto exit_free; + } + + /* init eeprom */ +#if (MULTIPAGE_SUPPORT == 1) + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &qsfp->eeprom.bin, SFF_8436_EEPROM_SIZE); +#else + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &qsfp->eeprom.bin); +#endif + if (status) { + goto exit_remove; + } + + *data = qsfp; + dev_info(&client->dev, "qsfp '%s'\n", client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &qsfp_group); +exit_free: + kfree(qsfp); +exit: + + return status; +} + +/* Platform dependent +++ */ +static int sfp_device_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int ret = 0; + struct sfp_port_data *data = NULL; + + if (client->addr != SFP_EEPROM_A0_I2C_ADDR) { + return -ENODEV; + } + + if (dev_id->driver_data < as7312_54x_port1 || dev_id->driver_data > as7312_54x_port54) { + return -ENXIO; + } + + data = kzalloc(sizeof(struct sfp_port_data), GFP_KERNEL); + if (!data) { + return -ENOMEM; + } + +#if (MULTIPAGE_SUPPORT == 1) + data->use_smbus = 0; + + /* Use I2C operations unless we're stuck with SMBus extensions. */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + data->use_smbus = I2C_SMBUS_I2C_BLOCK_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + data->use_smbus = I2C_SMBUS_WORD_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + data->use_smbus = I2C_SMBUS_BYTE_DATA; + } else { + ret = -EPFNOSUPPORT; + goto exit_kfree; + } + } + + if (!data->use_smbus || + (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_WORD_DATA) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + /* + * NOTE: AN-2079 + * Finisar recommends that the host implement 1 byte writes + * only since this module only supports 32 byte page boundaries. + * 2 byte writes are acceptable for PE and Vout changes per + * Application Note AN-2071. + */ + unsigned write_max = 1; + + if (write_max > io_limit) + write_max = io_limit; + if (data->use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) + write_max = I2C_SMBUS_BLOCK_MAX; + data->write_max = write_max; + + /* buffer (data + address at the beginning) */ + data->writebuf = kmalloc(write_max + 2, GFP_KERNEL); + if (!data->writebuf) { + ret = -ENOMEM; + goto exit_kfree; + } + } else { + dev_warn(&client->dev, + "cannot write due to controller restrictions."); + } + + if (data->use_smbus == I2C_SMBUS_WORD_DATA || + data->use_smbus == I2C_SMBUS_BYTE_DATA) { + dev_notice(&client->dev, "Falling back to %s reads, " + "performance will suffer\n", data->use_smbus == + I2C_SMBUS_WORD_DATA ? "word" : "byte"); + } +#endif + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->port = dev_id->driver_data; + data->client = client; + + if (dev_id->driver_data >= as7312_54x_port1 && dev_id->driver_data <= as7312_54x_port48) { + data->driver_type = DRIVER_TYPE_SFP_MSA; + ret = sfp_msa_probe(client, dev_id, &data->msa); + } + else { /* as7312_54x_portsfp49 ~ as7312_54x_portsfp54 */ + data->driver_type = DRIVER_TYPE_QSFP; + ret = qsfp_probe(client, dev_id, &data->qsfp); + } + + if (ret < 0) { + goto exit_kfree_buf; + } + + + return ret; + +exit_kfree_buf: +#if (MULTIPAGE_SUPPORT == 1) + if (data->writebuf) kfree(data->writebuf); +#endif + +exit_kfree: + kfree(data); + return ret; +} +/* Platform dependent --- */ + +static int sfp_msa_remove(struct i2c_client *client, struct sfp_msa_data *data) +{ + sfp_sysfs_eeprom_cleanup(&client->dev.kobj, &data->eeprom.bin); +#if (MULTIPAGE_SUPPORT == 1) + i2c_unregister_device(data->ddm_client); +#endif + sysfs_remove_group(&client->dev.kobj, &sfp_msa_group); + kfree(data); + return 0; +} + +static int qfp_remove(struct i2c_client *client, struct qsfp_data *data) +{ + sfp_sysfs_eeprom_cleanup(&client->dev.kobj, &data->eeprom.bin); + sysfs_remove_group(&client->dev.kobj, &qsfp_group); + kfree(data); + return 0; +} + +static int sfp_device_remove(struct i2c_client *client) +{ + int ret = 0; + struct sfp_port_data *data = i2c_get_clientdata(client); + + switch (data->driver_type) { + case DRIVER_TYPE_SFP_MSA: + return sfp_msa_remove(client, data->msa); + case DRIVER_TYPE_QSFP: + return qfp_remove(client, data->qsfp); + } + +#if (MULTIPAGE_SUPPORT == 1) + if (data->writebuf) + kfree(data->writebuf); +#endif + kfree(data); + return ret; +} + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +static struct i2c_driver sfp_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = sfp_device_probe, + .remove = sfp_device_remove, + .id_table = sfp_device_id, + .address_list = normal_i2c, +}; + +static int __init sfp_init(void) +{ + return i2c_add_driver(&sfp_driver); +} + +static void __exit sfp_exit(void) +{ + i2c_del_driver(&sfp_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton as7312_54x_sfp driver"); +MODULE_LICENSE("GPL"); + +module_init(sfp_init); +module_exit(sfp_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_i2c_cpld.c b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_i2c_cpld.c new file mode 100644 index 000000000000..67ecd8e036a9 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/accton_i2c_cpld.c @@ -0,0 +1,1219 @@ +/* + * Copyright (C) Brandon Chuang + * + * This module supports the accton cpld that hold the channel select + * mechanism for other i2c slave devices, such as SFP. + * This includes the: + * Accton as7312_54x CPLD1/CPLD2/CPLD3 + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I2C_RW_RETRY_COUNT 10 +#define I2C_RW_RETRY_INTERVAL 60 /* ms */ + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +enum cpld_type { + as7312_54x_cpld1, + as7312_54x_cpld2, + as7312_54x_cpld3 +}; + +struct as7312_54x_cpld_data { + enum cpld_type type; + struct device *hwmon_dev; + struct mutex update_lock; +}; + +static const struct i2c_device_id as7312_54x_cpld_id[] = { + { "as7312_54x_cpld1", as7312_54x_cpld1 }, + { "as7312_54x_cpld2", as7312_54x_cpld2 }, + { "as7312_54x_cpld3", as7312_54x_cpld3 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, as7312_54x_cpld_id); + +#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index +#define TRANSCEIVER_TXDISABLE_ATTR_ID(index) MODULE_TXDISABLE_##index +#define TRANSCEIVER_RXLOS_ATTR_ID(index) MODULE_RXLOS_##index +#define TRANSCEIVER_TXFAULT_ATTR_ID(index) MODULE_TXFAULT_##index +#define TRANSCEIVER_RESET_ATTR_ID(index) MODULE_RESET_##index + +enum as7312_54x_cpld1_sysfs_attributes { + CPLD_VERSION, + ACCESS, + MODULE_PRESENT_ALL, + MODULE_RXLOS_ALL, + /* transceiver attributes */ + TRANSCEIVER_PRESENT_ATTR_ID(1), + TRANSCEIVER_PRESENT_ATTR_ID(2), + TRANSCEIVER_PRESENT_ATTR_ID(3), + TRANSCEIVER_PRESENT_ATTR_ID(4), + TRANSCEIVER_PRESENT_ATTR_ID(5), + TRANSCEIVER_PRESENT_ATTR_ID(6), + TRANSCEIVER_PRESENT_ATTR_ID(7), + TRANSCEIVER_PRESENT_ATTR_ID(8), + TRANSCEIVER_PRESENT_ATTR_ID(9), + TRANSCEIVER_PRESENT_ATTR_ID(10), + TRANSCEIVER_PRESENT_ATTR_ID(11), + TRANSCEIVER_PRESENT_ATTR_ID(12), + TRANSCEIVER_PRESENT_ATTR_ID(13), + TRANSCEIVER_PRESENT_ATTR_ID(14), + TRANSCEIVER_PRESENT_ATTR_ID(15), + TRANSCEIVER_PRESENT_ATTR_ID(16), + TRANSCEIVER_PRESENT_ATTR_ID(17), + TRANSCEIVER_PRESENT_ATTR_ID(18), + TRANSCEIVER_PRESENT_ATTR_ID(19), + TRANSCEIVER_PRESENT_ATTR_ID(20), + TRANSCEIVER_PRESENT_ATTR_ID(21), + TRANSCEIVER_PRESENT_ATTR_ID(22), + TRANSCEIVER_PRESENT_ATTR_ID(23), + TRANSCEIVER_PRESENT_ATTR_ID(24), + TRANSCEIVER_PRESENT_ATTR_ID(25), + TRANSCEIVER_PRESENT_ATTR_ID(26), + TRANSCEIVER_PRESENT_ATTR_ID(27), + TRANSCEIVER_PRESENT_ATTR_ID(28), + TRANSCEIVER_PRESENT_ATTR_ID(29), + TRANSCEIVER_PRESENT_ATTR_ID(30), + TRANSCEIVER_PRESENT_ATTR_ID(31), + TRANSCEIVER_PRESENT_ATTR_ID(32), + TRANSCEIVER_PRESENT_ATTR_ID(33), + TRANSCEIVER_PRESENT_ATTR_ID(34), + TRANSCEIVER_PRESENT_ATTR_ID(35), + TRANSCEIVER_PRESENT_ATTR_ID(36), + TRANSCEIVER_PRESENT_ATTR_ID(37), + TRANSCEIVER_PRESENT_ATTR_ID(38), + TRANSCEIVER_PRESENT_ATTR_ID(39), + TRANSCEIVER_PRESENT_ATTR_ID(40), + TRANSCEIVER_PRESENT_ATTR_ID(41), + TRANSCEIVER_PRESENT_ATTR_ID(42), + TRANSCEIVER_PRESENT_ATTR_ID(43), + TRANSCEIVER_PRESENT_ATTR_ID(44), + TRANSCEIVER_PRESENT_ATTR_ID(45), + TRANSCEIVER_PRESENT_ATTR_ID(46), + TRANSCEIVER_PRESENT_ATTR_ID(47), + TRANSCEIVER_PRESENT_ATTR_ID(48), + TRANSCEIVER_PRESENT_ATTR_ID(49), + TRANSCEIVER_PRESENT_ATTR_ID(50), + TRANSCEIVER_PRESENT_ATTR_ID(51), + TRANSCEIVER_PRESENT_ATTR_ID(52), + TRANSCEIVER_PRESENT_ATTR_ID(53), + TRANSCEIVER_PRESENT_ATTR_ID(54), + TRANSCEIVER_RESET_ATTR_ID(49), + TRANSCEIVER_RESET_ATTR_ID(50), + TRANSCEIVER_RESET_ATTR_ID(51), + TRANSCEIVER_RESET_ATTR_ID(52), + TRANSCEIVER_RESET_ATTR_ID(53), + TRANSCEIVER_RESET_ATTR_ID(54), + TRANSCEIVER_TXDISABLE_ATTR_ID(1), + TRANSCEIVER_TXDISABLE_ATTR_ID(2), + TRANSCEIVER_TXDISABLE_ATTR_ID(3), + TRANSCEIVER_TXDISABLE_ATTR_ID(4), + TRANSCEIVER_TXDISABLE_ATTR_ID(5), + TRANSCEIVER_TXDISABLE_ATTR_ID(6), + TRANSCEIVER_TXDISABLE_ATTR_ID(7), + TRANSCEIVER_TXDISABLE_ATTR_ID(8), + TRANSCEIVER_TXDISABLE_ATTR_ID(9), + TRANSCEIVER_TXDISABLE_ATTR_ID(10), + TRANSCEIVER_TXDISABLE_ATTR_ID(11), + TRANSCEIVER_TXDISABLE_ATTR_ID(12), + TRANSCEIVER_TXDISABLE_ATTR_ID(13), + TRANSCEIVER_TXDISABLE_ATTR_ID(14), + TRANSCEIVER_TXDISABLE_ATTR_ID(15), + TRANSCEIVER_TXDISABLE_ATTR_ID(16), + TRANSCEIVER_TXDISABLE_ATTR_ID(17), + TRANSCEIVER_TXDISABLE_ATTR_ID(18), + TRANSCEIVER_TXDISABLE_ATTR_ID(19), + TRANSCEIVER_TXDISABLE_ATTR_ID(20), + TRANSCEIVER_TXDISABLE_ATTR_ID(21), + TRANSCEIVER_TXDISABLE_ATTR_ID(22), + TRANSCEIVER_TXDISABLE_ATTR_ID(23), + TRANSCEIVER_TXDISABLE_ATTR_ID(24), + TRANSCEIVER_TXDISABLE_ATTR_ID(25), + TRANSCEIVER_TXDISABLE_ATTR_ID(26), + TRANSCEIVER_TXDISABLE_ATTR_ID(27), + TRANSCEIVER_TXDISABLE_ATTR_ID(28), + TRANSCEIVER_TXDISABLE_ATTR_ID(29), + TRANSCEIVER_TXDISABLE_ATTR_ID(30), + TRANSCEIVER_TXDISABLE_ATTR_ID(31), + TRANSCEIVER_TXDISABLE_ATTR_ID(32), + TRANSCEIVER_TXDISABLE_ATTR_ID(33), + TRANSCEIVER_TXDISABLE_ATTR_ID(34), + TRANSCEIVER_TXDISABLE_ATTR_ID(35), + TRANSCEIVER_TXDISABLE_ATTR_ID(36), + TRANSCEIVER_TXDISABLE_ATTR_ID(37), + TRANSCEIVER_TXDISABLE_ATTR_ID(38), + TRANSCEIVER_TXDISABLE_ATTR_ID(39), + TRANSCEIVER_TXDISABLE_ATTR_ID(40), + TRANSCEIVER_TXDISABLE_ATTR_ID(41), + TRANSCEIVER_TXDISABLE_ATTR_ID(42), + TRANSCEIVER_TXDISABLE_ATTR_ID(43), + TRANSCEIVER_TXDISABLE_ATTR_ID(44), + TRANSCEIVER_TXDISABLE_ATTR_ID(45), + TRANSCEIVER_TXDISABLE_ATTR_ID(46), + TRANSCEIVER_TXDISABLE_ATTR_ID(47), + TRANSCEIVER_TXDISABLE_ATTR_ID(48), + TRANSCEIVER_RXLOS_ATTR_ID(1), + TRANSCEIVER_RXLOS_ATTR_ID(2), + TRANSCEIVER_RXLOS_ATTR_ID(3), + TRANSCEIVER_RXLOS_ATTR_ID(4), + TRANSCEIVER_RXLOS_ATTR_ID(5), + TRANSCEIVER_RXLOS_ATTR_ID(6), + TRANSCEIVER_RXLOS_ATTR_ID(7), + TRANSCEIVER_RXLOS_ATTR_ID(8), + TRANSCEIVER_RXLOS_ATTR_ID(9), + TRANSCEIVER_RXLOS_ATTR_ID(10), + TRANSCEIVER_RXLOS_ATTR_ID(11), + TRANSCEIVER_RXLOS_ATTR_ID(12), + TRANSCEIVER_RXLOS_ATTR_ID(13), + TRANSCEIVER_RXLOS_ATTR_ID(14), + TRANSCEIVER_RXLOS_ATTR_ID(15), + TRANSCEIVER_RXLOS_ATTR_ID(16), + TRANSCEIVER_RXLOS_ATTR_ID(17), + TRANSCEIVER_RXLOS_ATTR_ID(18), + TRANSCEIVER_RXLOS_ATTR_ID(19), + TRANSCEIVER_RXLOS_ATTR_ID(20), + TRANSCEIVER_RXLOS_ATTR_ID(21), + TRANSCEIVER_RXLOS_ATTR_ID(22), + TRANSCEIVER_RXLOS_ATTR_ID(23), + TRANSCEIVER_RXLOS_ATTR_ID(24), + TRANSCEIVER_RXLOS_ATTR_ID(25), + TRANSCEIVER_RXLOS_ATTR_ID(26), + TRANSCEIVER_RXLOS_ATTR_ID(27), + TRANSCEIVER_RXLOS_ATTR_ID(28), + TRANSCEIVER_RXLOS_ATTR_ID(29), + TRANSCEIVER_RXLOS_ATTR_ID(30), + TRANSCEIVER_RXLOS_ATTR_ID(31), + TRANSCEIVER_RXLOS_ATTR_ID(32), + TRANSCEIVER_RXLOS_ATTR_ID(33), + TRANSCEIVER_RXLOS_ATTR_ID(34), + TRANSCEIVER_RXLOS_ATTR_ID(35), + TRANSCEIVER_RXLOS_ATTR_ID(36), + TRANSCEIVER_RXLOS_ATTR_ID(37), + TRANSCEIVER_RXLOS_ATTR_ID(38), + TRANSCEIVER_RXLOS_ATTR_ID(39), + TRANSCEIVER_RXLOS_ATTR_ID(40), + TRANSCEIVER_RXLOS_ATTR_ID(41), + TRANSCEIVER_RXLOS_ATTR_ID(42), + TRANSCEIVER_RXLOS_ATTR_ID(43), + TRANSCEIVER_RXLOS_ATTR_ID(44), + TRANSCEIVER_RXLOS_ATTR_ID(45), + TRANSCEIVER_RXLOS_ATTR_ID(46), + TRANSCEIVER_RXLOS_ATTR_ID(47), + TRANSCEIVER_RXLOS_ATTR_ID(48), + TRANSCEIVER_TXFAULT_ATTR_ID(1), + TRANSCEIVER_TXFAULT_ATTR_ID(2), + TRANSCEIVER_TXFAULT_ATTR_ID(3), + TRANSCEIVER_TXFAULT_ATTR_ID(4), + TRANSCEIVER_TXFAULT_ATTR_ID(5), + TRANSCEIVER_TXFAULT_ATTR_ID(6), + TRANSCEIVER_TXFAULT_ATTR_ID(7), + TRANSCEIVER_TXFAULT_ATTR_ID(8), + TRANSCEIVER_TXFAULT_ATTR_ID(9), + TRANSCEIVER_TXFAULT_ATTR_ID(10), + TRANSCEIVER_TXFAULT_ATTR_ID(11), + TRANSCEIVER_TXFAULT_ATTR_ID(12), + TRANSCEIVER_TXFAULT_ATTR_ID(13), + TRANSCEIVER_TXFAULT_ATTR_ID(14), + TRANSCEIVER_TXFAULT_ATTR_ID(15), + TRANSCEIVER_TXFAULT_ATTR_ID(16), + TRANSCEIVER_TXFAULT_ATTR_ID(17), + TRANSCEIVER_TXFAULT_ATTR_ID(18), + TRANSCEIVER_TXFAULT_ATTR_ID(19), + TRANSCEIVER_TXFAULT_ATTR_ID(20), + TRANSCEIVER_TXFAULT_ATTR_ID(21), + TRANSCEIVER_TXFAULT_ATTR_ID(22), + TRANSCEIVER_TXFAULT_ATTR_ID(23), + TRANSCEIVER_TXFAULT_ATTR_ID(24), + TRANSCEIVER_TXFAULT_ATTR_ID(25), + TRANSCEIVER_TXFAULT_ATTR_ID(26), + TRANSCEIVER_TXFAULT_ATTR_ID(27), + TRANSCEIVER_TXFAULT_ATTR_ID(28), + TRANSCEIVER_TXFAULT_ATTR_ID(29), + TRANSCEIVER_TXFAULT_ATTR_ID(30), + TRANSCEIVER_TXFAULT_ATTR_ID(31), + TRANSCEIVER_TXFAULT_ATTR_ID(32), + TRANSCEIVER_TXFAULT_ATTR_ID(33), + TRANSCEIVER_TXFAULT_ATTR_ID(34), + TRANSCEIVER_TXFAULT_ATTR_ID(35), + TRANSCEIVER_TXFAULT_ATTR_ID(36), + TRANSCEIVER_TXFAULT_ATTR_ID(37), + TRANSCEIVER_TXFAULT_ATTR_ID(38), + TRANSCEIVER_TXFAULT_ATTR_ID(39), + TRANSCEIVER_TXFAULT_ATTR_ID(40), + TRANSCEIVER_TXFAULT_ATTR_ID(41), + TRANSCEIVER_TXFAULT_ATTR_ID(42), + TRANSCEIVER_TXFAULT_ATTR_ID(43), + TRANSCEIVER_TXFAULT_ATTR_ID(44), + TRANSCEIVER_TXFAULT_ATTR_ID(45), + TRANSCEIVER_TXFAULT_ATTR_ID(46), + TRANSCEIVER_TXFAULT_ATTR_ID(47), + TRANSCEIVER_TXFAULT_ATTR_ID(48), +}; + +/* sysfs attributes for hwmon + */ +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_present_all(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_rxlos_all(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t set_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t show_version(struct device *dev, struct device_attribute *da, + char *buf); +static int as7312_54x_cpld_read_internal(struct i2c_client *client, u8 reg); +static int as7312_54x_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value); + +/* transceiver attributes */ +#define DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, show_status, NULL, MODULE_PRESENT_##index) +#define DECLARE_TRANSCEIVER_PRESENT_ATTR(index) &sensor_dev_attr_module_present_##index.dev_attr.attr + +#define DECLARE_TRANSCEIVER_RESET_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_reset_##index, S_IRUGO | S_IWUSR, show_status, set_reset, MODULE_RESET_##index) +#define DECLARE_TRANSCEIVER_RESET_ATTR(index) &sensor_dev_attr_module_reset_##index.dev_attr.attr + +#define DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_tx_disable_##index, S_IRUGO | S_IWUSR, show_status, set_tx_disable, MODULE_TXDISABLE_##index); \ + static SENSOR_DEVICE_ATTR(module_rx_los_##index, S_IRUGO, show_status, NULL, MODULE_RXLOS_##index); \ + static SENSOR_DEVICE_ATTR(module_tx_fault_##index, S_IRUGO, show_status, NULL, MODULE_TXFAULT_##index) +#define DECLARE_SFP_TRANSCEIVER_ATTR(index) \ + &sensor_dev_attr_module_tx_disable_##index.dev_attr.attr, \ + &sensor_dev_attr_module_rx_los_##index.dev_attr.attr, \ + &sensor_dev_attr_module_tx_fault_##index.dev_attr.attr + +static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_version, NULL, CPLD_VERSION); +static SENSOR_DEVICE_ATTR(access, S_IWUSR, NULL, access, ACCESS); +/* transceiver attributes */ +static SENSOR_DEVICE_ATTR(module_present_all, S_IRUGO, show_present_all, NULL, MODULE_PRESENT_ALL); +static SENSOR_DEVICE_ATTR(module_rx_los_all, S_IRUGO, show_rxlos_all, NULL, MODULE_RXLOS_ALL); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(1); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(2); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(3); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(4); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(5); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(6); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(7); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(8); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(9); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(10); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(11); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(12); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(13); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(14); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(15); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(16); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(17); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(18); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(19); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(20); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(21); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(22); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(23); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(24); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(25); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(26); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(27); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(28); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(29); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(30); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(31); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(32); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(33); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(34); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(35); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(36); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(37); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(38); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(39); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(40); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(41); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(42); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(43); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(44); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(45); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(46); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(47); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(48); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(49); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(50); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(51); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(52); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(53); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(54); + +DECLARE_TRANSCEIVER_RESET_SENSOR_DEVICE_ATTR(49); +DECLARE_TRANSCEIVER_RESET_SENSOR_DEVICE_ATTR(50); +DECLARE_TRANSCEIVER_RESET_SENSOR_DEVICE_ATTR(51); +DECLARE_TRANSCEIVER_RESET_SENSOR_DEVICE_ATTR(52); +DECLARE_TRANSCEIVER_RESET_SENSOR_DEVICE_ATTR(53); +DECLARE_TRANSCEIVER_RESET_SENSOR_DEVICE_ATTR(54); + +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(1); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(2); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(3); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(4); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(5); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(6); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(7); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(8); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(9); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(10); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(11); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(12); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(13); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(14); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(15); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(16); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(17); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(18); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(19); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(20); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(21); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(22); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(23); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(24); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(25); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(26); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(27); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(28); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(29); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(30); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(31); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(32); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(33); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(34); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(35); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(36); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(37); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(38); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(39); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(40); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(41); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(42); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(43); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(44); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(45); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(46); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(47); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(48); + +static struct attribute *as7312_54x_cpld1_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + NULL +}; + +static const struct attribute_group as7312_54x_cpld1_group = { + .attrs = as7312_54x_cpld1_attributes, +}; + +static struct attribute *as7312_54x_cpld2_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + /* transceiver attributes */ + &sensor_dev_attr_module_present_all.dev_attr.attr, + &sensor_dev_attr_module_rx_los_all.dev_attr.attr, + DECLARE_TRANSCEIVER_PRESENT_ATTR(1), + DECLARE_TRANSCEIVER_PRESENT_ATTR(2), + DECLARE_TRANSCEIVER_PRESENT_ATTR(3), + DECLARE_TRANSCEIVER_PRESENT_ATTR(4), + DECLARE_TRANSCEIVER_PRESENT_ATTR(5), + DECLARE_TRANSCEIVER_PRESENT_ATTR(6), + DECLARE_TRANSCEIVER_PRESENT_ATTR(7), + DECLARE_TRANSCEIVER_PRESENT_ATTR(8), + DECLARE_TRANSCEIVER_PRESENT_ATTR(9), + DECLARE_TRANSCEIVER_PRESENT_ATTR(10), + DECLARE_TRANSCEIVER_PRESENT_ATTR(11), + DECLARE_TRANSCEIVER_PRESENT_ATTR(12), + DECLARE_TRANSCEIVER_PRESENT_ATTR(13), + DECLARE_TRANSCEIVER_PRESENT_ATTR(14), + DECLARE_TRANSCEIVER_PRESENT_ATTR(15), + DECLARE_TRANSCEIVER_PRESENT_ATTR(16), + DECLARE_TRANSCEIVER_PRESENT_ATTR(17), + DECLARE_TRANSCEIVER_PRESENT_ATTR(18), + DECLARE_TRANSCEIVER_PRESENT_ATTR(19), + DECLARE_TRANSCEIVER_PRESENT_ATTR(20), + DECLARE_TRANSCEIVER_PRESENT_ATTR(21), + DECLARE_TRANSCEIVER_PRESENT_ATTR(22), + DECLARE_TRANSCEIVER_PRESENT_ATTR(23), + DECLARE_TRANSCEIVER_PRESENT_ATTR(24), + DECLARE_TRANSCEIVER_PRESENT_ATTR(49), + DECLARE_TRANSCEIVER_PRESENT_ATTR(50), + DECLARE_TRANSCEIVER_PRESENT_ATTR(51), + DECLARE_TRANSCEIVER_PRESENT_ATTR(52), + DECLARE_TRANSCEIVER_RESET_ATTR(49), + DECLARE_TRANSCEIVER_RESET_ATTR(50), + DECLARE_TRANSCEIVER_RESET_ATTR(51), + DECLARE_TRANSCEIVER_RESET_ATTR(52), + DECLARE_SFP_TRANSCEIVER_ATTR(1), + DECLARE_SFP_TRANSCEIVER_ATTR(2), + DECLARE_SFP_TRANSCEIVER_ATTR(3), + DECLARE_SFP_TRANSCEIVER_ATTR(4), + DECLARE_SFP_TRANSCEIVER_ATTR(5), + DECLARE_SFP_TRANSCEIVER_ATTR(6), + DECLARE_SFP_TRANSCEIVER_ATTR(7), + DECLARE_SFP_TRANSCEIVER_ATTR(8), + DECLARE_SFP_TRANSCEIVER_ATTR(9), + DECLARE_SFP_TRANSCEIVER_ATTR(10), + DECLARE_SFP_TRANSCEIVER_ATTR(11), + DECLARE_SFP_TRANSCEIVER_ATTR(12), + DECLARE_SFP_TRANSCEIVER_ATTR(13), + DECLARE_SFP_TRANSCEIVER_ATTR(14), + DECLARE_SFP_TRANSCEIVER_ATTR(15), + DECLARE_SFP_TRANSCEIVER_ATTR(16), + DECLARE_SFP_TRANSCEIVER_ATTR(17), + DECLARE_SFP_TRANSCEIVER_ATTR(18), + DECLARE_SFP_TRANSCEIVER_ATTR(19), + DECLARE_SFP_TRANSCEIVER_ATTR(20), + DECLARE_SFP_TRANSCEIVER_ATTR(21), + DECLARE_SFP_TRANSCEIVER_ATTR(22), + DECLARE_SFP_TRANSCEIVER_ATTR(23), + DECLARE_SFP_TRANSCEIVER_ATTR(24), + NULL +}; + +static const struct attribute_group as7312_54x_cpld2_group = { + .attrs = as7312_54x_cpld2_attributes, +}; + +static struct attribute *as7312_54x_cpld3_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + /* transceiver attributes */ + &sensor_dev_attr_module_present_all.dev_attr.attr, + &sensor_dev_attr_module_rx_los_all.dev_attr.attr, + DECLARE_TRANSCEIVER_PRESENT_ATTR(25), + DECLARE_TRANSCEIVER_PRESENT_ATTR(26), + DECLARE_TRANSCEIVER_PRESENT_ATTR(27), + DECLARE_TRANSCEIVER_PRESENT_ATTR(28), + DECLARE_TRANSCEIVER_PRESENT_ATTR(29), + DECLARE_TRANSCEIVER_PRESENT_ATTR(30), + DECLARE_TRANSCEIVER_PRESENT_ATTR(31), + DECLARE_TRANSCEIVER_PRESENT_ATTR(32), + DECLARE_TRANSCEIVER_PRESENT_ATTR(33), + DECLARE_TRANSCEIVER_PRESENT_ATTR(34), + DECLARE_TRANSCEIVER_PRESENT_ATTR(35), + DECLARE_TRANSCEIVER_PRESENT_ATTR(36), + DECLARE_TRANSCEIVER_PRESENT_ATTR(37), + DECLARE_TRANSCEIVER_PRESENT_ATTR(38), + DECLARE_TRANSCEIVER_PRESENT_ATTR(39), + DECLARE_TRANSCEIVER_PRESENT_ATTR(40), + DECLARE_TRANSCEIVER_PRESENT_ATTR(41), + DECLARE_TRANSCEIVER_PRESENT_ATTR(42), + DECLARE_TRANSCEIVER_PRESENT_ATTR(43), + DECLARE_TRANSCEIVER_PRESENT_ATTR(44), + DECLARE_TRANSCEIVER_PRESENT_ATTR(45), + DECLARE_TRANSCEIVER_PRESENT_ATTR(46), + DECLARE_TRANSCEIVER_PRESENT_ATTR(47), + DECLARE_TRANSCEIVER_PRESENT_ATTR(48), + DECLARE_TRANSCEIVER_PRESENT_ATTR(53), + DECLARE_TRANSCEIVER_PRESENT_ATTR(54), + DECLARE_TRANSCEIVER_RESET_ATTR(53), + DECLARE_TRANSCEIVER_RESET_ATTR(54), + DECLARE_SFP_TRANSCEIVER_ATTR(25), + DECLARE_SFP_TRANSCEIVER_ATTR(26), + DECLARE_SFP_TRANSCEIVER_ATTR(27), + DECLARE_SFP_TRANSCEIVER_ATTR(28), + DECLARE_SFP_TRANSCEIVER_ATTR(29), + DECLARE_SFP_TRANSCEIVER_ATTR(30), + DECLARE_SFP_TRANSCEIVER_ATTR(31), + DECLARE_SFP_TRANSCEIVER_ATTR(32), + DECLARE_SFP_TRANSCEIVER_ATTR(33), + DECLARE_SFP_TRANSCEIVER_ATTR(34), + DECLARE_SFP_TRANSCEIVER_ATTR(35), + DECLARE_SFP_TRANSCEIVER_ATTR(36), + DECLARE_SFP_TRANSCEIVER_ATTR(37), + DECLARE_SFP_TRANSCEIVER_ATTR(38), + DECLARE_SFP_TRANSCEIVER_ATTR(39), + DECLARE_SFP_TRANSCEIVER_ATTR(40), + DECLARE_SFP_TRANSCEIVER_ATTR(41), + DECLARE_SFP_TRANSCEIVER_ATTR(42), + DECLARE_SFP_TRANSCEIVER_ATTR(43), + DECLARE_SFP_TRANSCEIVER_ATTR(44), + DECLARE_SFP_TRANSCEIVER_ATTR(45), + DECLARE_SFP_TRANSCEIVER_ATTR(46), + DECLARE_SFP_TRANSCEIVER_ATTR(47), + DECLARE_SFP_TRANSCEIVER_ATTR(48), + NULL +}; + +static const struct attribute_group as7312_54x_cpld3_group = { + .attrs = as7312_54x_cpld3_attributes, +}; + +static ssize_t show_present_all(struct device *dev, struct device_attribute *da, + char *buf) +{ + int i, status; + u8 values[4] = {0}; + u8 regs[] = {0x9, 0xA, 0xB, 0x18}; + struct i2c_client *client = to_i2c_client(dev); + struct as7312_54x_cpld_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + status = as7312_54x_cpld_read_internal(client, regs[i]); + + if (status < 0) { + goto exit; + } + + values[i] = ~(u8)status; + } + + mutex_unlock(&data->update_lock); + + /* Return values 1 -> 54 in order */ + if (data->type == as7312_54x_cpld2) { + values[3] &= 0xF; + } + else { /* as7312_54x_cpld3 */ + values[3] &= 0x3; + } + + return sprintf(buf, "%.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], values[3]); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_rxlos_all(struct device *dev, struct device_attribute *da, + char *buf) +{ + int i, status; + u8 values[3] = {0}; + u8 regs[] = {0x12, 0x13, 0x14}; + struct i2c_client *client = to_i2c_client(dev); + struct as7312_54x_cpld_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + status = as7312_54x_cpld_read_internal(client, regs[i]); + + if (status < 0) { + goto exit; + } + + values[i] = (u8)status; + } + + mutex_unlock(&data->update_lock); + + /* Return values 1 -> 24 in order */ + return sprintf(buf, "%.2x %.2x %.2x\n", values[0], values[1], values[2]); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7312_54x_cpld_data *data = i2c_get_clientdata(client); + int status = 0; + u8 reg = 0, mask = 0, revert = 0; + + switch (attr->index) { + case MODULE_PRESENT_1 ... MODULE_PRESENT_8: + reg = 0x9; + mask = 0x1 << (attr->index - MODULE_PRESENT_1); + break; + case MODULE_PRESENT_9 ... MODULE_PRESENT_16: + reg = 0xA; + mask = 0x1 << (attr->index - MODULE_PRESENT_9); + break; + case MODULE_PRESENT_17 ... MODULE_PRESENT_24: + reg = 0xB; + mask = 0x1 << (attr->index - MODULE_PRESENT_17); + break; + case MODULE_PRESENT_25 ... MODULE_PRESENT_32: + reg = 0x9; + mask = 0x1 << (attr->index - MODULE_PRESENT_25); + break; + case MODULE_PRESENT_33 ... MODULE_PRESENT_40: + reg = 0xA; + mask = 0x1 << (attr->index - MODULE_PRESENT_33); + break; + case MODULE_PRESENT_41 ... MODULE_PRESENT_48: + reg = 0xB; + mask = 0x1 << (attr->index - MODULE_PRESENT_41); + break; + case MODULE_PRESENT_49: + reg = 0x18; + mask = 0x1; + break; + case MODULE_PRESENT_50: + reg = 0x18; + mask = 0x2; + break; + case MODULE_PRESENT_51: + reg = 0x18; + mask = 0x4; + break; + case MODULE_PRESENT_52: + reg = 0x18; + mask = 0x8; + break; + case MODULE_PRESENT_53: + reg = 0x18; + mask = 0x1; + break; + case MODULE_PRESENT_54: + reg = 0x18; + mask = 0x2; + break; + + case MODULE_RESET_49 ... MODULE_RESET_54: + reg = 0x17; + mask = 1 << ((attr->index - MODULE_PRESENT_49)%4); + break; + + case MODULE_TXFAULT_1 ... MODULE_TXFAULT_8: + reg = 0xC; + mask = 0x1 << (attr->index - MODULE_TXFAULT_1); + break; + case MODULE_TXFAULT_9 ... MODULE_TXFAULT_16: + reg = 0xD; + mask = 0x1 << (attr->index - MODULE_TXFAULT_9); + break; + case MODULE_TXFAULT_17 ... MODULE_TXFAULT_24: + reg = 0xE; + mask = 0x1 << (attr->index - MODULE_TXFAULT_17); + break; + case MODULE_TXFAULT_25 ... MODULE_TXFAULT_32: + reg = 0xC; + mask = 0x1 << (attr->index - MODULE_TXFAULT_25); + break; + case MODULE_TXFAULT_33 ... MODULE_TXFAULT_40: + reg = 0xD; + mask = 0x1 << (attr->index - MODULE_TXFAULT_33); + break; + case MODULE_TXFAULT_41 ... MODULE_TXFAULT_48: + reg = 0xE; + mask = 0x1 << (attr->index - MODULE_TXFAULT_41); + break; + case MODULE_TXDISABLE_1 ... MODULE_TXDISABLE_8: + reg = 0xF; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_1); + break; + case MODULE_TXDISABLE_9 ... MODULE_TXDISABLE_16: + reg = 0x10; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_9); + break; + case MODULE_TXDISABLE_17 ... MODULE_TXDISABLE_24: + reg = 0x11; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_17); + break; + case MODULE_TXDISABLE_25 ... MODULE_TXDISABLE_32: + reg = 0xF; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_25); + break; + case MODULE_TXDISABLE_33 ... MODULE_TXDISABLE_40: + reg = 0x10; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_33); + break; + case MODULE_TXDISABLE_41 ... MODULE_TXDISABLE_48: + reg = 0x11; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_41); + break; + case MODULE_RXLOS_1 ... MODULE_RXLOS_8: + reg = 0x12; + mask = 0x1 << (attr->index - MODULE_RXLOS_1); + break; + case MODULE_RXLOS_9 ... MODULE_RXLOS_16: + reg = 0x13; + mask = 0x1 << (attr->index - MODULE_RXLOS_9); + break; + case MODULE_RXLOS_17 ... MODULE_RXLOS_24: + reg = 0x14; + mask = 0x1 << (attr->index - MODULE_RXLOS_17); + break; + case MODULE_RXLOS_25 ... MODULE_RXLOS_32: + reg = 0x12; + mask = 0x1 << (attr->index - MODULE_RXLOS_25); + break; + case MODULE_RXLOS_33 ... MODULE_RXLOS_40: + reg = 0x13; + mask = 0x1 << (attr->index - MODULE_RXLOS_33); + break; + case MODULE_RXLOS_41 ... MODULE_RXLOS_48: + reg = 0x14; + mask = 0x1 << (attr->index - MODULE_RXLOS_41); + break; + default: + return 0; + } + + if (attr->index >= MODULE_PRESENT_1 && attr->index <= MODULE_PRESENT_54) { + revert = 1; + } + + mutex_lock(&data->update_lock); + status = as7312_54x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", revert ? !(status & mask) : !!(status & mask)); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t set_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7312_54x_cpld_data *data = i2c_get_clientdata(client); + long reset; + int status; + u8 reg = 0, mask = 0; + + status = kstrtol(buf, 10, &reset); + if (status) { + return status; + } + + switch (attr->index) { + case MODULE_RESET_49 ... MODULE_RESET_54: + reg = 0x17; + mask = 1 << ((attr->index - MODULE_RESET_49)%4); + break; + default: + return 0; + } + + /* Read current status */ + mutex_lock(&data->update_lock); + status = as7312_54x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + + /* Update reset status */ + if (reset) { + status |= mask; + } + else { + status &= ~mask; + } + status = as7312_54x_cpld_write_internal(client, reg, status); + if (unlikely(status < 0)) { + goto exit; + } + + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + + + + +static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7312_54x_cpld_data *data = i2c_get_clientdata(client); + long disable; + int status; + u8 reg = 0, mask = 0; + + status = kstrtol(buf, 10, &disable); + if (status) { + return status; + } + + switch (attr->index) { + case MODULE_TXDISABLE_1 ... MODULE_TXDISABLE_8: + reg = 0xF; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_1); + break; + case MODULE_TXDISABLE_9 ... MODULE_TXDISABLE_16: + reg = 0x10; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_9); + break; + case MODULE_TXDISABLE_17 ... MODULE_TXDISABLE_24: + reg = 0x11; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_17); + break; + case MODULE_TXDISABLE_25 ... MODULE_TXDISABLE_32: + reg = 0xF; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_25); + break; + case MODULE_TXDISABLE_33 ... MODULE_TXDISABLE_40: + reg = 0x10; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_33); + break; + case MODULE_TXDISABLE_41 ... MODULE_TXDISABLE_48: + reg = 0x11; + mask = 0x1 << (attr->index - MODULE_TXDISABLE_41); + break; + default: + return 0; + } + + /* Read current status */ + mutex_lock(&data->update_lock); + status = as7312_54x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + + /* Update tx_disable status */ + if (disable) { + status |= mask; + } + else { + status &= ~mask; + } + + status = as7312_54x_cpld_write_internal(client, reg, status); + if (unlikely(status < 0)) { + goto exit; + } + + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int status; + u32 addr, val; + struct i2c_client *client = to_i2c_client(dev); + struct as7312_54x_cpld_data *data = i2c_get_clientdata(client); + + if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) { + return -EINVAL; + } + + if (addr > 0xFF || val > 0xFF) { + return -EINVAL; + } + + mutex_lock(&data->update_lock); + status = as7312_54x_cpld_write_internal(client, addr, val); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static void as7312_54x_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void as7312_54x_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + + mutex_unlock(&list_lock); +} + +static ssize_t show_version(struct device *dev, struct device_attribute *attr, char *buf) +{ + int val = 0; + struct i2c_client *client = to_i2c_client(dev); + + val = i2c_smbus_read_byte_data(client, 0x1); + + if (val < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x1) err %d\n", client->addr, val); + } + + return sprintf(buf, "%d", val); +} + +/* + * I2C init/probing/exit functions + */ +static int as7312_54x_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + struct as7312_54x_cpld_data *data; + int ret = -ENODEV; + const struct attribute_group *group = NULL; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) + goto exit; + + data = kzalloc(sizeof(struct as7312_54x_cpld_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->type = id->driver_data; + + /* Register sysfs hooks */ + switch (data->type) { + case as7312_54x_cpld1: + group = &as7312_54x_cpld1_group; + break; + case as7312_54x_cpld2: + group = &as7312_54x_cpld2_group; + break; + case as7312_54x_cpld3: + group = &as7312_54x_cpld3_group; + break; + default: + break; + } + + if (group) { + ret = sysfs_create_group(&client->dev.kobj, group); + if (ret) { + goto exit_free; + } + } + + as7312_54x_cpld_add_client(client); + return 0; + +exit_free: + kfree(data); +exit: + return ret; +} + +static int as7312_54x_cpld_remove(struct i2c_client *client) +{ + struct as7312_54x_cpld_data *data = i2c_get_clientdata(client); + const struct attribute_group *group = NULL; + + as7312_54x_cpld_remove_client(client); + + /* Remove sysfs hooks */ + switch (data->type) { + case as7312_54x_cpld1: + group = &as7312_54x_cpld1_group; + break; + case as7312_54x_cpld2: + group = &as7312_54x_cpld2_group; + break; + case as7312_54x_cpld3: + group = &as7312_54x_cpld3_group; + break; + default: + break; + } + + if (group) { + sysfs_remove_group(&client->dev.kobj, group); + } + + kfree(data); + + return 0; +} + +static int as7312_54x_cpld_read_internal(struct i2c_client *client, u8 reg) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, reg); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static int as7312_54x_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_write_byte_data(client, reg, value); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +int as7312_54x_cpld_read(unsigned short cpld_addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = as7312_54x_cpld_read_internal(cpld_node->client, reg); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as7312_54x_cpld_read); + +int as7312_54x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = as7312_54x_cpld_write_internal(cpld_node->client, reg, value); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as7312_54x_cpld_write); + +static struct i2c_driver as7312_54x_cpld_driver = { + .driver = { + .name = "as7312_54x_cpld", + .owner = THIS_MODULE, + }, + .probe = as7312_54x_cpld_probe, + .remove = as7312_54x_cpld_remove, + .id_table = as7312_54x_cpld_id, +}; + +static int __init as7312_54x_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&as7312_54x_cpld_driver); +} + +static void __exit as7312_54x_cpld_exit(void) +{ + i2c_del_driver(&as7312_54x_cpld_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("Accton I2C CPLD driver"); +MODULE_LICENSE("GPL"); + +module_init(as7312_54x_cpld_init); +module_exit(as7312_54x_cpld_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/ym2651y.c b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/ym2651y.c new file mode 120000 index 000000000000..f4d67640ccc3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/modules/ym2651y.c @@ -0,0 +1 @@ +../../common/modules/ym2651y.c \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/service/as7312-platform-monitor.service b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/service/as7312-platform-monitor.service new file mode 100755 index 000000000000..b79357665bed --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/service/as7312-platform-monitor.service @@ -0,0 +1,17 @@ +[Unit] +Description=Accton AS7312-54X Platform Monitoring service +Before=pmon.service +After=sysinit.target +DefaultDependencies=no + +[Service] +ExecStartPre=/usr/local/bin/accton_as7312_util.py install +ExecStart=/usr/local/bin/accton_as7312_monitor.py +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/setup.py b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/setup.py new file mode 100755 index 000000000000..f0518d07689d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/setup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env pytho + +import os +import sys +from setuptools import setup +os.listdir + +setup( + name='as7312_54x', + version='1.0', + description='Module to initialize Accton AS7312-54X platforms', + + packages=['as7312_54x'], + package_dir={'as7312_54x': 'as7312-54x/classes'}, +) + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/utils/README b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/utils/README new file mode 100755 index 000000000000..66f31a030423 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/utils/README @@ -0,0 +1,117 @@ +Copyright (C) 2016 Accton Networks, Inc. + +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 3 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, see . + +Contents of this package: + patch - files under patch/ is for kernel and ONIE installer + for the kernel: + config-accton-as7312_54x.patch + for kernel configuration. + driver-i2c-muxes-pca954x-always-deselect.patch + for i2c_mux deselects after transaction. + driver-patches-for-accton-as7312-fan-psu-cpld.patch + for as7312's fan/psu/cpld/led/sfp drivers. + for ONIE: + onie_installer-accton-AS7312-54X.patch + for console port setting and copy util script o rootfs. + module - Contains source code of as7312 kernel driver modules. + +The late Sonic building scripts, pushed @Dec 5 2016, will automatically +create a docker container and run building process under it. +User is not necessary to handle docker environment creation. + +1. Download sonic-buildimage environment. + - Run "git clone https://github.com/Azure/sonic-buildimage". + - cd to sonic-buildimage and run "git submodule update --init --recursive". +2. Build kernel + - cd ./src/sonic-linux-kernel + - Copy patches and series from patch/kernel of this release to + sonic-linux-kernel/patch. + - Build kernel by "make". + - The built kernel package, linux-image-3.16.0-5-amd64_3.16.51-3+deb8u1_amd64.deb + , is generated. +3. Build installer + - Change directory back to sonic-buildimage/. + - Get onie_installer-accton-AS7312-54X.patch" from patch/installer. + - Change setting for AS7312-54X by patching build_image.sh. + "patch -p1 < onie_installer-accton-AS7312-54X.patch" + !!NOTICE, patching onie_installer-accton-AS7312-54X.patch comments out the + "git status" checking at build_image.sh. + - The account and password of installed OS can be given at rules/config. + The default user and password are "admin" & "YourPaSsWoRd" respectively. + - Run "make configure PLATFORM=broadcom" + - Copy the built kernel debian package to target/debs/. + The file is linux-image-3.16.0-5-amd64_*_amd64.deb under directory + src/sonic-linux-kernel/. + - Run "make target/sonic-generic.bin" + - Get the installer, target/sonic-generic.bin, to target machine and install. + +All Linux kernel code is licensed under the GPLv1. All other code is +licensed under the GPLv3. Please see the LICENSE file for copies of +both licenses. + +The code for integacting with Accton AS7312-54X has 2 parts, +kernel drivers and operational script. +The kernel drivers of peripherals are under module/ directory. +1. These drivers are patched into kernel by + driver-patches-for-accton-as7312-fan-psu-cpld.patch + Or you can build the driver under module/ by setting environment variable, + KERNEL_SRC, to proper linux built directory and run make. + It may be sonic-linux-kernel/linux-3.*/debian/build/build_amd64_none_amd64/. +2. A operational script, accton_as7312_util.py, for device initializatian and + peripheral accessing should be installed at /usr/bin. + This script is generated by onie_installer-accton-AS7312-54X.patch. + It's done by patching onie_installer-accton-AS7312-54X.patch at build-image. + Run "accton_as7312_util.py install" to install drivers. + +To initialize the system, run "accton_as7312_util.py install". +To clean up the drivers & devices, run "accton_as7312_util.py clean". +To dump information of sensors, run "accton_as7312_util.py show". +To dump SFP EEPROM, run "accton_as7312_util.py sff". +To set fan speed, run "accton_as7312_util.py set fan". +To enable/disable SFP emission, run "accton_as7312_util.py set sfp". +To set system LEDs' color, run "accton_as7312_util.py set led" +For more information, run "accton_as7312_util.py --help". + +==================================================================== +Besides applying accton_as7312_util.py to access peripherals, you can +access peripherals by sysfs nodes directly after the installation is run. + +System LED: + There are 5 system LEDs at the lower-left corner of front panel. + They are loc, diag, fan, ps1, and ps2. + The sysfs interface color mappings are as follows: + Brightness: + 0 => off + 1 => green + 2 => amber + 3 => red + 4 => blue + But not all colors are available for each LED. + +Fan Control: + There are 10 fans inside 5 fan modules. + All fans share 1 duty setting, ranged from 0~100. + +Thermal sensers: + 3 temperature sensors are controlled by the lm75 kernel modules. + +PSUs: + There 2 power supplies slot at the left/right side of the back. + Once if a PSU is not plugged, the status of it is shown failed. + +There are 48 SFP+ and 6 QSFP modules are equipped. +Before operating on PSU and QSFP+, please make sure it is well plugged. +Otherwise, operation is going to fail. + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/utils/accton_as7312_monitor.py b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/utils/accton_as7312_monitor.py new file mode 100755 index 000000000000..bdab0d4b8bff --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/utils/accton_as7312_monitor.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# 1/10/2018: Jostar modify for as7716_32 +# 2/27/2018: Roy Lee modify for as7312_54x +# ------------------------------------------------------------------ + +try: + import os + import sys, getopt + import subprocess + import click + import imp + import logging + import logging.config + import types + import time # this is only being used as part of the example + import traceback + from tabulate import tabulate + from as7312_54x.fanutil import FanUtil + from as7312_54x.thermalutil import ThermalUtil +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + +# Deafults +VERSION = '1.0' +FUNCTION_NAME = 'accton_as7312_monitor' + +global log_file +global log_level + +# (LM75_1+ LM75_2+ LM75_3) is LM75 at i2c addresses 0x48, 0x49, and 0x4A. +# TMP = (LM75_1+ LM75_2+ LM75_3)/3 +#1. If TMP < 35, All fans run with duty 31.25%. +#2. If TMP>=35 or the temperature of any one of fan is higher than 40, +# All fans run with duty 50% +#3. If TMP >= 40 or the temperature of any one of fan is higher than 45, +# All fans run with duty 62.5%. +#4. If TMP >= 45 or the temperature of any one of fan is higher than 50, +# All fans run with duty 100%. +#5. Any one of 6 fans is fault, set duty = 100%. +#6. Direction factor. If it is B2F direction, duty + 12%. + + # MISC: + # 1.Check single LM75 before applied average. + # 2.If no matched fan speed is found from the policy, + # use FAN_DUTY_CYCLE_MIN as default speed + # Get current temperature + # 4.Decision 3: Decide new fan speed depend on fan direction/current fan speed/temperature + + + + +# Make a class we can use to capture stdout and sterr in the log +class accton_as7312_monitor(object): + # static temp var + _ori_temp = 0 + _new_perc = 0 + _ori_perc = 0 + + def __init__(self, log_file, log_level): + """Needs a logger and a logger level.""" + # set up logging to file + logging.basicConfig( + filename=log_file, + filemode='w', + level=log_level, + format= '[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s', + datefmt='%H:%M:%S' + ) + + # set up logging to console + if log_level == logging.DEBUG: + console = logging.StreamHandler() + console.setLevel(log_level) + formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + + logging.debug('SET. logfile:%s / loglevel:%d', log_file, log_level) + + def manage_fans(self): + max_duty = 100 + fan_policy_f2b = { + 0: [32, 0, 105000], + 1: [50, 105000, 120000], + 2: [63, 120000, 135000], + 3: [max_duty, 135000, sys.maxsize], + } + fan_policy_b2f = { + 0: [44, 0, 105000], + 1: [63, 105000, 120000], + 2: [75, 120000, 135000], + 3: [max_duty, 135000, sys.maxsize], + } + fan_policy_single = { + 0: 40000, + 1: 45000, + 2: 50000, + } + + thermal = ThermalUtil() + fan = FanUtil() + for x in range(fan.get_idx_fan_start(), fan.get_num_fans()+1): + fan_status = fan.get_fan_status(x) + if fan_status is None: + logging.debug('INFO. SET new_perc to %d (FAN stauts is None. fan_num:%d)', max_duty, x) + return False + if fan_status is False: + logging.debug('INFO. SET new_perc to %d (FAN fault. fan_num:%d)', max_duty, x) + fan.set_fan_duty_cycle(max_duty) + return True + #logging.debug('INFO. fan_status is True (fan_num:%d)', x) + + fan_dir=fan.get_fan_dir(1) + if fan_dir == 1: + fan_policy = fan_policy_f2b + else: + fan_policy = fan_policy_b2f + + #Decide fan duty by if any of sensors > fan_policy_single. + new_duty_cycle = fan_policy[0][0] + for x in range(thermal.get_idx_thermal_start(), thermal.get_num_thermals()+1): + single_thm = thermal._get_thermal_node_val(x) + for y in range(0, len(fan_policy_single)): + if single_thm > fan_policy_single[y]: + if fan_policy[y+1][0] > new_duty_cycle: + new_duty_cycle = fan_policy[y+1][0] + logging.debug('INFO. Single thermal sensor %d with temp %d > %d , new_duty_cycle=%d', + x, single_thm, fan_policy_single[y], new_duty_cycle) + single_result = new_duty_cycle + + + #Find if current duty matched any of define duty. + #If not, set it to highest one. + cur_duty_cycle = fan.get_fan_duty_cycle() + for x in range(0, len(fan_policy)): + if cur_duty_cycle == fan_policy[x][0]: + break + if x == len(fan_policy) : + fan.set_fan_duty_cycle(fan_policy[0][0]) + cur_duty_cycle = max_duty + + #Decide fan duty by if sum of sensors falls into any of fan_policy{} + get_temp = thermal.get_thermal_temp() + new_duty_cycle = cur_duty_cycle + for x in range(0, len(fan_policy)): + y = len(fan_policy) - x -1 #checked from highest + if get_temp > fan_policy[y][1] and get_temp < fan_policy[y][2] : + new_duty_cycle= fan_policy[y][0] + logging.debug('INFO. Sum of temp %d > %d , new_duty_cycle=%d', get_temp, fan_policy[y][1], new_duty_cycle) + + sum_result = new_duty_cycle + if (sum_result>single_result): + new_duty_cycle = sum_result; + else: + new_duty_cycle = single_result + + logging.debug('INFO. Final duty_cycle=%d', new_duty_cycle) + if(new_duty_cycle != cur_duty_cycle): + fan.set_fan_duty_cycle(new_duty_cycle) + return True + +def main(argv): + log_file = '%s.log' % FUNCTION_NAME + log_level = logging.INFO + if len(sys.argv) != 1: + try: + opts, args = getopt.getopt(argv,'hdl:',['lfile=']) + except getopt.GetoptError: + print 'Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + for opt, arg in opts: + if opt == '-h': + print 'Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + elif opt in ('-d', '--debug'): + log_level = logging.DEBUG + elif opt in ('-l', '--lfile'): + log_file = arg + + monitor = accton_as7312_monitor(log_file, log_level) + + # Loop forever, doing something useful hopefully: + while True: + monitor.manage_fans() + time.sleep(10) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54x/utils/accton_as7312_util.py b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/utils/accton_as7312_util.py new file mode 100755 index 000000000000..b8107c47afea --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54x/utils/accton_as7312_util.py @@ -0,0 +1,588 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 Accton Networks, Inc. +# +# 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 3 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, see . + +""" +Usage: %(scriptName)s [options] command object + +options: + -h | --help : this help message + -d | --debug : run with debug mode + -f | --force : ignore error during installation or clean +command: + install : install drivers and generate related sysfs nodes + clean : uninstall drivers and remove related sysfs nodes + show : show all systen status + sff : dump SFP eeprom + set : change board setting with fan|led|sfp +""" + +import os +import commands +import sys, getopt +import logging +import re +import time +from collections import namedtuple + + + + +PROJECT_NAME = 'as7312_54x' +version = '0.1.0' +verbose = False +DEBUG = False +args = [] +ALL_DEVICE = {} +DEVICE_NO = {'led':5, 'fan':6,'thermal':4, 'psu':2, 'sfp':54} +FORCE = 0 +#logging.basicConfig(filename= PROJECT_NAME+'.log', filemode='w',level=logging.DEBUG) +#logging.basicConfig(level=logging.INFO) + + +if DEBUG == True: + print sys.argv[0] + print 'ARGV :', sys.argv[1:] + + +def main(): + global DEBUG + global args + global FORCE + + if len(sys.argv)<2: + show_help() + + options, args = getopt.getopt(sys.argv[1:], 'hdf', ['help', + 'debug', + 'force', + ]) + if DEBUG == True: + print options + print args + print len(sys.argv) + + for opt, arg in options: + if opt in ('-h', '--help'): + show_help() + elif opt in ('-d', '--debug'): + DEBUG = True + logging.basicConfig(level=logging.INFO) + elif opt in ('-f', '--force'): + FORCE = 1 + else: + logging.info('no option') + for arg in args: + if arg == 'install': + do_install() + elif arg == 'clean': + do_uninstall() + elif arg == 'show': + device_traversal() + elif arg == 'sff': + if len(args)!=2: + show_eeprom_help() + elif int(args[1]) ==0 or int(args[1]) > DEVICE_NO['sfp']: + show_eeprom_help() + else: + show_eeprom(args[1]) + return + elif arg == 'set': + if len(args)<3: + show_set_help() + else: + set_device(args[1:]) + return + else: + show_help() + + + return 0 + +def show_help(): + print __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]} + sys.exit(0) + +def show_set_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print cmd +" [led|sfp|fan]" + print " use \""+ cmd + " led 0-4 \" to set led color" + print " use \""+ cmd + " fan 0-100\" to set fan duty percetage" + print " use \""+ cmd + " sfp 1-54 {0|1}\" to set sfp# tx_disable" + sys.exit(0) + +def show_eeprom_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print " use \""+ cmd + " 1-54 \" to dump sfp# eeprom" + sys.exit(0) + +def my_log(txt): + if DEBUG == True: + print "[ROY]"+txt + return + +def log_os_system(cmd, show): + logging.info('Run :'+cmd) + status, output = commands.getstatusoutput(cmd) + my_log (cmd +"with result:" + str(status)) + my_log (" output:"+output) + if status: + logging.info('Failed :'+cmd) + if show: + print('Failed :'+cmd) + return status, output + +def driver_check(): + ret, lsmod = log_os_system("lsmod| grep accton", 0) + logging.info('mods:'+lsmod) + if len(lsmod) ==0: + return False + return True + + + +kos = [ +'modprobe i2c_dev', +'modprobe i2c_mux_pca954x force_deselect_on_exit=1', +'modprobe accton_i2c_cpld' , +'modprobe ym2651y' , +'modprobe accton_as7312_54x_fan' , +'modprobe optoe' , +'modprobe accton_as7312_54x_leds' , +'modprobe accton_as7312_54x_psu' ] + +def driver_install(): + global FORCE + status, output = log_os_system("depmod", 1) + for i in range(0,len(kos)): + status, output = log_os_system(kos[i], 1) + if status: + if FORCE == 0: + return status + return 0 + +def driver_uninstall(): + global FORCE + for i in range(0,len(kos)): + rm = kos[-(i+1)].replace("modprobe", "modprobe -rq") + rm = rm.replace("insmod", "rmmod") + lst = rm.split(" ") + if len(lst) > 3: + del(lst[3]) + rm = " ".join(lst) + status, output = log_os_system(rm, 1) + if status: + if FORCE == 0: + return status + return 0 + +led_prefix ='/sys/class/leds/accton_'+PROJECT_NAME+'_led::' +hwmon_types = {'led': ['diag','fan','loc','psu1','psu2']} +hwmon_nodes = {'led': ['brightness'] } +hwmon_prefix ={'led': led_prefix} + +i2c_prefix = '/sys/bus/i2c/devices/' +i2c_bus = {'fan': ['2-0066'] , + 'thermal': ['3-0048','3-0049', '3-004a', '3-004b'] , + 'psu': ['10-0051','11-0053'], + 'sfp': ['-0050']} +i2c_nodes = {'fan': ['present', 'front_speed_rpm', 'rear_speed_rpm'] , + 'thermal': ['hwmon/hwmon*/temp1_input'] , + 'psu': ['psu_present ', 'psu_power_good'] , + 'sfp': ['sfp_is_present', 'sfp_tx_disable']} + +sfp_map = [18,19,20,21,22,23,24,25,26,27, + 28,29,30,31,32,33,34,35,36,37, + 38,39,40,41,42,43,44,45,46,47, + 48,49,50,51,52,53,54,55,56,57, + 58,59,60,61,62,63,64,65,66,67, + 68,69,70,71] + +qsfp_start = 48 + +mknod =[ +'echo pca9548 0x76 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-0/new_device' , +'echo pca9548 0x72 > /sys/bus/i2c/devices/i2c-1/new_device' , +'echo pca9548 0x73 > /sys/bus/i2c/devices/i2c-1/new_device' , +'echo pca9548 0x74 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x75 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x76 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo 24c02 0x57 > /sys/bus/i2c/devices/i2c-1/new_device', + +'echo as7312_54x_fan 0x66 > /sys/bus/i2c/devices/i2c-2/new_device ', +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-3/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-3/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-3/new_device', +'echo lm75 0x4b > /sys/bus/i2c/devices/i2c-3/new_device', +'echo as7312_54x_psu1 0x53 > /sys/bus/i2c/devices/i2c-11/new_device', +'echo ym2651 0x5b > /sys/bus/i2c/devices/i2c-11/new_device', +'echo as7312_54x_psu2 0x50 > /sys/bus/i2c/devices/i2c-10/new_device', +'echo ym2651 0x58 > /sys/bus/i2c/devices/i2c-10/new_device', +'echo as7312_54x_cpld1 0x60 > /sys/bus/i2c/devices/i2c-4/new_device', +'echo as7312_54x_cpld2 0x62 > /sys/bus/i2c/devices/i2c-5/new_device', +'echo as7312_54x_cpld3 0x64 > /sys/bus/i2c/devices/i2c-6/new_device'] + +mknod2 =[ +'echo pca9548 0x76 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-1/new_device' , +'echo pca9548 0x72 > /sys/bus/i2c/devices/i2c-0/new_device' , +'echo pca9548 0x73 > /sys/bus/i2c/devices/i2c-0/new_device' , +'echo pca9548 0x74 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo pca9548 0x75 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo pca9548 0x76 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo 24c02 0x57 > /sys/bus/i2c/devices/i2c-0/new_device', + +'echo as7312_54x_fan 0x66 > /sys/bus/i2c/devices/i2c-2/new_device ', +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-3/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-3/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-3/new_device', +'echo lm75 0x4b > /sys/bus/i2c/devices/i2c-3/new_device', +'echo as7312_54x_psu1 0x53 > /sys/bus/i2c/devices/i2c-11/new_device', +'echo ym2651 0x5b > /sys/bus/i2c/devices/i2c-11/new_device', +'echo as7312_54x_psu2 0x50 > /sys/bus/i2c/devices/i2c-10/new_device', +'echo ym2651 0x58 > /sys/bus/i2c/devices/i2c-10/new_device', +'echo as7312_54x_cpld1 0x60 > /sys/bus/i2c/devices/i2c-4/new_device', +'echo as7312_54x_cpld2 0x62 > /sys/bus/i2c/devices/i2c-5/new_device', +'echo as7312_54x_cpld3 0x64 > /sys/bus/i2c/devices/i2c-6/new_device'] + + + +def i2c_order_check(): + # i2c bus 0 and 1 might be installed in different order. + # Here check if 0x70 is exist @ i2c-1 + tmp = "echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-1/new_device" + status, output = log_os_system(tmp, 0) + if not device_exist(): + order = 1 + else: + order = 0 + tmp = "echo 0x70 > /sys/bus/i2c/devices/i2c-1/delete_device" + status, output = log_os_system(tmp, 0) + return order + +def device_install(): + global FORCE + + order = i2c_order_check() + + # if 0x70 is not exist @i2c-1, use reversed bus order + if order: + for i in range(0,len(mknod2)): + #for pca954x need times to built new i2c buses + if mknod2[i].find('pca954') != -1: + time.sleep(1) + + status, output = log_os_system(mknod2[i], 1) + if status: + print output + if FORCE == 0: + return status + else: + for i in range(0,len(mknod)): + #for pca954x need times to built new i2c buses + if mknod[i].find('pca954') != -1: + time.sleep(1) + + status, output = log_os_system(mknod[i], 1) + if status: + print output + if FORCE == 0: + return status + for i in range(0,len(sfp_map)): + if i < qsfp_start: + status, output =log_os_system("echo optoe2 0x50 > /sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/new_device", 1) + else: + status, output =log_os_system("echo optoe1 0x50 > /sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/new_device", 1) + if status: + print output + if FORCE == 0: + return status + return + +def device_uninstall(): + global FORCE + + status, output =log_os_system("ls /sys/bus/i2c/devices/1-0076", 0) + if status==0: + I2C_ORDER=1 + else: + I2C_ORDER=0 + + for i in range(0,len(sfp_map)): + target = "/sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/delete_device" + status, output =log_os_system("echo 0x50 > "+ target, 1) + if status: + print output + if FORCE == 0: + return status + + if I2C_ORDER==0: + nodelist = mknod + else: + nodelist = mknod2 + + for i in range(len(nodelist)): + target = nodelist[-(i+1)] + temp = target.split() + del temp[1] + temp[-1] = temp[-1].replace('new_device', 'delete_device') + status, output = log_os_system(" ".join(temp), 1) + if status: + print output + if FORCE == 0: + return status + + return + +def system_ready(): + if driver_check() == False: + return False + if not device_exist(): + return False + return True + +def do_install(): + print "Checking system...." + if driver_check() == False: + print "No driver, installing...." + status = driver_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" drivers detected...." + if not device_exist(): + print "No device, installing...." + status = device_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" devices detected...." + return + +def do_uninstall(): + print "Checking system...." + if not device_exist(): + print PROJECT_NAME.upper() +" has no device installed...." + else: + print "Removing device...." + status = device_uninstall() + if status: + if FORCE == 0: + return status + + if driver_check()== False : + print PROJECT_NAME.upper() +" has no driver installed...." + else: + print "Removing installed driver...." + status = driver_uninstall() + if status: + if FORCE == 0: + return status + + return + +def devices_info(): + global DEVICE_NO + global ALL_DEVICE + global i2c_bus, hwmon_types + for key in DEVICE_NO: + ALL_DEVICE[key]= {} + for i in range(0,DEVICE_NO[key]): + ALL_DEVICE[key][key+str(i+1)] = [] + + for key in i2c_bus: + buses = i2c_bus[key] + nodes = i2c_nodes[key] + for i in range(0,len(buses)): + for j in range(0,len(nodes)): + if 'fan' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ buses[i]+"/fan"+str(k+1)+"_"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + elif 'sfp' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ str(sfp_map[k])+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + else: + node = key+str(i+1) + path = i2c_prefix+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + + for key in hwmon_types: + itypes = hwmon_types[key] + nodes = hwmon_nodes[key] + for i in range(0,len(itypes)): + for j in range(0,len(nodes)): + node = key+"_"+itypes[i] + path = hwmon_prefix[key]+ itypes[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][ key+str(i+1)].append(path) + + #show dict all in the order + if DEBUG == True: + for i in sorted(ALL_DEVICE.keys()): + print(i+": ") + for j in sorted(ALL_DEVICE[i].keys()): + print(" "+j) + for k in (ALL_DEVICE[i][j]): + print(" "+" "+k) + return + +def show_eeprom(index): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + node = ALL_DEVICE['sfp'] ['sfp'+str(index)][0] + node = node.replace(node.split("/")[-1], 'sfp_eeprom') + # check if got hexdump command in current environment + ret, log = log_os_system("which hexdump", 0) + ret, log2 = log_os_system("which busybox hexdump", 0) + if len(log): + hex_cmd = 'hexdump' + elif len(log2): + hex_cmd = ' busybox hexdump' + else: + log = 'Failed : no hexdump cmd!!' + logging.info(log) + print log + return 1 + + print node + ":" + ret, log = log_os_system("cat "+node+"| "+hex_cmd+" -C", 1) + if ret==0: + print log + else: + print "**********device no found**********" + return + +def set_device(args): + global DEVICE_NO + global ALL_DEVICE + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + + if args[0]=='led': + if int(args[1])>4: + show_set_help() + return + #print ALL_DEVICE['led'] + for i in range(0,len(ALL_DEVICE['led'])): + for k in (ALL_DEVICE['led']['led'+str(i+1)]): + ret, log = log_os_system("echo "+args[1]+" >"+k, 1) + if ret: + return ret + elif args[0]=='fan': + if int(args[1])>100: + show_set_help() + return + #print ALL_DEVICE['fan'] + #fan1~6 is all fine, all fan share same setting + node = ALL_DEVICE['fan'] ['fan1'][0] + node = node.replace(node.split("/")[-1], 'fan_duty_cycle_percentage') + ret, log = log_os_system("cat "+ node, 1) + if ret==0: + print ("Previous fan duty: " + log.strip() +"%") + ret, log = log_os_system("echo "+args[1]+" >"+node, 1) + if ret==0: + print ("Current fan duty: " + args[1] +"%") + return ret + elif args[0]=='sfp': + if int(args[1])> DEVICE_NO[args[0]] or int(args[1])==0: + show_set_help() + return + if len(args)<2: + show_set_help() + return + + if int(args[2])>1: + show_set_help() + return + + #print ALL_DEVICE[args[0]] + for i in range(0,len(ALL_DEVICE[args[0]])): + for j in ALL_DEVICE[args[0]][args[0]+str(args[1])]: + if j.find('tx_disable')!= -1: + ret, log = log_os_system("echo "+args[2]+" >"+ j, 1) + if ret: + return ret + + return + +#get digits inside a string. +#Ex: 31 for "sfp31" +def get_value(input): + digit = re.findall('\d+', input) + return int(digit[0]) + +def device_traversal(): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + for i in sorted(ALL_DEVICE.keys()): + print("============================================") + print(i.upper()+": ") + print("============================================") + + for j in sorted(ALL_DEVICE[i].keys(), key=get_value): + print " "+j+":", + for k in (ALL_DEVICE[i][j]): + ret, log = log_os_system("cat "+k, 0) + func = k.split("/")[-1].strip() + func = re.sub(j+'_','',func,1) + func = re.sub(i.lower()+'_','',func,1) + if ret==0: + print func+"="+log+" ", + else: + print func+"="+"X"+" ", + print + print("----------------------------------------------------------------") + + + print + return + +def device_exist(): + ret1, log = log_os_system("ls "+i2c_prefix+"*0070", 0) + ret2, log = log_os_system("ls "+i2c_prefix+"i2c-2", 0) + return not(ret1 or ret2) + +if __name__ == "__main__": + main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/classes/__init__.py b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/classes/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/classes/fanutil.py b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/classes/fanutil.py new file mode 100755 index 000000000000..06ebbb777920 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/classes/fanutil.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# 1/10/2018: Jostar modify for as7716_32 +# 3/32/2018: Roy Lee modify for as7326_56x +# ------------------------------------------------------------------ + +try: + import time + import logging + from collections import namedtuple +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + + +class FanUtil(object): + """Platform-specific FanUtil class""" + + FAN_NUM_ON_MAIN_BROAD = 6 + FAN_NUM_1_IDX = 1 + FAN_NUM_2_IDX = 2 + FAN_NUM_3_IDX = 3 + FAN_NUM_4_IDX = 4 + FAN_NUM_5_IDX = 5 + FAN_NUM_6_IDX = 6 + + FAN_NODE_NUM_OF_MAP = 2 + FAN_NODE_FAULT_IDX_OF_MAP = 1 + #FAN_NODE_SPEED_IDX_OF_MAP = 2 + FAN_NODE_DIR_IDX_OF_MAP = 2 + #FAN_NODE_DUTY_IDX_OF_MAP = 4 + #FANR_NODE_FAULT_IDX_OF_MAP = 5 + + BASE_VAL_PATH = '/sys/bus/i2c/devices/11-0066/{0}' + FAN_DUTY_PATH = '/sys/bus/i2c/devices/11-0066/fan_duty_cycle_percentage' + + #logfile = '' + #loglevel = logging.INFO + + """ Dictionary where + key1 = fan id index (integer) starting from 1 + key2 = fan node index (interger) starting from 1 + value = path to fan device file (string) """ + _fan_to_device_path_mapping = {} + +#fan1_direction +#fan1_fault +#fan1_present + + #(FAN_NUM_2_IDX, FAN_NODE_DUTY_IDX_OF_MAP): 'fan2_duty_cycle_percentage', + _fan_to_device_node_mapping = { + (FAN_NUM_1_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan1_fault', + (FAN_NUM_1_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan1_direction', + + (FAN_NUM_2_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan2_fault', + (FAN_NUM_2_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan2_direction', + + (FAN_NUM_3_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan3_fault', + (FAN_NUM_3_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan3_direction', + + (FAN_NUM_4_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan4_fault', + (FAN_NUM_4_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan4_direction', + + (FAN_NUM_5_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan5_fault', + (FAN_NUM_5_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan5_direction', + + (FAN_NUM_6_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan6_fault', + (FAN_NUM_6_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan6_direction', + } + + def _get_fan_to_device_node(self, fan_num, node_num): + return self._fan_to_device_node_mapping[(fan_num, node_num)] + + def _get_fan_node_val(self, fan_num, node_num): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num:%d', fan_num) + return None + + if node_num < self.FAN_NODE_FAULT_IDX_OF_MAP or node_num > self.FAN_NODE_NUM_OF_MAP: + logging.debug('GET. Parameter error. node_num:%d', node_num) + return None + + device_path = self.get_fan_to_device_path(fan_num, node_num) + + try: + val_file = open(device_path, 'r') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + content = val_file.readline().rstrip() + + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return int(content) + + def _set_fan_node_val(self, fan_num, node_num, val): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num:%d', fan_num) + return None + + if node_num < self.FAN_NODE_FAULT_IDX_OF_MAP or node_num > self.FAN_NODE_NUM_OF_MAP: + logging.debug('GET. Parameter error. node_num:%d', node_num) + return None + + content = str(val) + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + device_path = self.get_fan_to_device_path(fan_num, node_num) + try: + val_file = open(device_path, 'w') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + val_file.write(content) + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return True + + def __init__(self): + fan_path = self.BASE_VAL_PATH + + for fan_num in range(self.FAN_NUM_1_IDX, self.FAN_NUM_ON_MAIN_BROAD+1): + for node_num in range(self.FAN_NODE_FAULT_IDX_OF_MAP, self.FAN_NODE_NUM_OF_MAP+1): + self._fan_to_device_path_mapping[(fan_num, node_num)] = fan_path.format( + self._fan_to_device_node_mapping[(fan_num, node_num)]) + + def get_num_fans(self): + return self.FAN_NUM_ON_MAIN_BROAD + + def get_idx_fan_start(self): + return self.FAN_NUM_1_IDX + + def get_num_nodes(self): + return self.FAN_NODE_NUM_OF_MAP + + def get_idx_node_start(self): + return self.FAN_NODE_FAULT_IDX_OF_MAP + + def get_size_node_map(self): + return len(self._fan_to_device_node_mapping) + + def get_size_path_map(self): + return len(self._fan_to_device_path_mapping) + + def get_fan_to_device_path(self, fan_num, node_num): + return self._fan_to_device_path_mapping[(fan_num, node_num)] + + def get_fan_fault(self, fan_num): + return self._get_fan_node_val(fan_num, self.FAN_NODE_FAULT_IDX_OF_MAP) + + #def get_fan_speed(self, fan_num): + # return self._get_fan_node_val(fan_num, self.FAN_NODE_SPEED_IDX_OF_MAP) + + def get_fan_dir(self, fan_num): + return self._get_fan_node_val(fan_num, self.FAN_NODE_DIR_IDX_OF_MAP) + + def get_fan_duty_cycle(self): + #duty_path = self.FAN_DUTY_PATH + try: + val_file = open(self.FAN_DUTY_PATH) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + content = val_file.readline().rstrip() + val_file.close() + + return int(content) + #self._get_fan_node_val(fan_num, self.FAN_NODE_DUTY_IDX_OF_MAP) +#static u32 reg_val_to_duty_cycle(u8 reg_val) +#{ +# reg_val &= FAN_DUTY_CYCLE_REG_MASK; +# return ((u32)(reg_val+1) * 625 + 75)/ 100; +#} +# + def set_fan_duty_cycle(self, val): + + try: + fan_file = open(self.FAN_DUTY_PATH, 'r+') + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + #val = ((val + 1 ) * 625 +75 ) / 100 + fan_file.write(str(val)) + fan_file.close() + return True + + #def get_fanr_fault(self, fan_num): + # return self._get_fan_node_val(fan_num, self.FANR_NODE_FAULT_IDX_OF_MAP) + + def get_fanr_speed(self, fan_num): + return self._get_fan_node_val(fan_num, self.FANR_NODE_SPEED_IDX_OF_MAP) + + def get_fan_status(self, fan_num): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num, %d', fan_num) + return None + + if self.get_fan_fault(fan_num) is not None and self.get_fan_fault(fan_num) > 0: + logging.debug('GET. FAN fault. fan_num, %d', fan_num) + return False + + #if self.get_fanr_fault(fan_num) is not None and self.get_fanr_fault(fan_num) > 0: + # logging.debug('GET. FANR fault. fan_num, %d', fan_num) + # return False + + return True + +#def main(): +# fan = FanUtil() +# +# print 'get_size_node_map : %d' % fan.get_size_node_map() +# print 'get_size_path_map : %d' % fan.get_size_path_map() +# for x in range(fan.get_idx_fan_start(), fan.get_num_fans()+1): +# for y in range(fan.get_idx_node_start(), fan.get_num_nodes()+1): +# print fan.get_fan_to_device_path(x, y) +# +#if __name__ == '__main__': +# main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/classes/thermalutil.py b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/classes/thermalutil.py new file mode 100755 index 000000000000..e4329b8b9783 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/classes/thermalutil.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# 1/10/2018:Jostar modify for as7716_32x +# 3/23/2018: Roy Lee modify for as7326_56x +# ------------------------------------------------------------------ + +try: + import time + import logging + import glob + from collections import namedtuple +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + + +class ThermalUtil(object): + """Platform-specific ThermalUtil class""" + + THERMAL_NUM_ON_MAIN_BROAD = 3 + THERMAL_NUM_1_IDX = 1 # 1_ON_MAIN_BROAD + THERMAL_NUM_2_IDX = 2 # 2_ON_MAIN_BROAD + THERMAL_NUM_3_IDX = 3 # 3_ON_MAIN_BROAD + + BASE_VAL_PATH = '/sys/bus/i2c/devices/{0}-00{1}/hwmon/hwmon*/temp1_input' + + """ Dictionary where + key1 = thermal id index (integer) starting from 1 + value = path to fan device file (string) """ + _thermal_to_device_path_mapping = {} + + _thermal_to_device_node_mapping = { + THERMAL_NUM_1_IDX: ['15', '48'], + THERMAL_NUM_2_IDX: ['15', '49'], + THERMAL_NUM_3_IDX: ['15', '4a'], + } + + def __init__(self): + thermal_path = self.BASE_VAL_PATH + + for x in range(self.THERMAL_NUM_1_IDX, self.THERMAL_NUM_ON_MAIN_BROAD+1): + self._thermal_to_device_path_mapping[x] = thermal_path.format( + self._thermal_to_device_node_mapping[x][0], + self._thermal_to_device_node_mapping[x][1]) + + def _get_thermal_node_val(self, thermal_num): + if thermal_num < self.THERMAL_NUM_1_IDX or thermal_num > self.THERMAL_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. thermal_num, %d', thermal_num) + return None + + device_path = self.get_thermal_to_device_path(thermal_num) + for filename in glob.glob(device_path): + try: + val_file = open(filename, 'r') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + content = val_file.readline().rstrip() + + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return int(content) + + + def get_num_thermals(self): + return self.THERMAL_NUM_ON_MAIN_BROAD + + def get_idx_thermal_start(self): + return self.THERMAL_NUM_1_IDX + + def get_size_node_map(self): + return len(self._thermal_to_device_node_mapping) + + def get_size_path_map(self): + return len(self._thermal_to_device_path_mapping) + + def get_thermal_to_device_path(self, thermal_num): + return self._thermal_to_device_path_mapping[thermal_num] + + def get_thermal_1_val(self): + return self._get_thermal_node_val(self.THERMAL_NUM_1_IDX) + + def get_thermal_2_val(self): + return self._get_thermal_node_val(self.THERMAL_NUM_2_IDX) + def get_thermal_temp(self): + return (self._get_thermal_node_val(self.THERMAL_NUM_1_IDX) + self._get_thermal_node_val(self.THERMAL_NUM_2_IDX) +self._get_thermal_node_val(self.THERMAL_NUM_3_IDX)) + +#def main(): +# thermal = ThermalUtil() +# +# print 'get_size_node_map : %d' % thermal.get_size_node_map() +# print 'get_size_path_map : %d' % thermal.get_size_path_map() +# for x in range(thermal.get_idx_thermal_start(), thermal.get_num_thermals()+1): +# print thermal.get_thermal_to_device_path(x) +# +#if __name__ == '__main__': +# main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/Makefile b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/Makefile new file mode 100755 index 000000000000..85c066571c2f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/Makefile @@ -0,0 +1,17 @@ +ifneq ($(KERNELRELEASE),) +obj-m:= accton_i2c_cpld.o \ + accton_as7326_56x_fan.o accton_as7326_56x_leds.o \ + accton_as7326_56x_psu.o ym2651y.o + +else +ifeq (,$(KERNEL_SRC)) +$(error KERNEL_SRC is not defined) +else +KERNELDIR:=$(KERNEL_SRC) +endif +PWD:=$(shell pwd) +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules +clean: + rm -rf *.o *.mod.o *.mod.o *.ko .*cmd .tmp_versions Module.markers Module.symvers modules.order +endif diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/accton_as7326_56x_fan.c b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/accton_as7326_56x_fan.c new file mode 100755 index 000000000000..606019f3ea44 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/accton_as7326_56x_fan.c @@ -0,0 +1,815 @@ +/* + * A hwmon driver for the Accton as7326 56x fan + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as7326_56x_fan" + +#define NUM_THERMAL_SENSORS (3) /* Get sum of this number of sensors.*/ +#define THERMAL_SENSORS_DRIVER "lm75" +#define THERMAL_SENSORS_ADDRS {0x48, 0x49, 0x4a} + +#define IN +#define OUT + +static struct as7326_56x_fan_data *as7326_56x_fan_update_device(struct device *dev); +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t get_enable(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t get_sys_temp(struct device *dev, struct device_attribute *da, char *buf); +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +/* fan related data, the index should match sysfs_fan_attributes + */ +static const u8 fan_reg[] = { + 0x0F, /* fan 1-6 present status */ + 0x10, /* fan 1-6 direction(0:F2B 1:B2F) */ + 0x11, /* fan PWM(for all fan) */ + 0x12, /* front fan 1 speed(rpm) */ + 0x13, /* front fan 2 speed(rpm) */ + 0x14, /* front fan 3 speed(rpm) */ + 0x15, /* front fan 4 speed(rpm) */ + 0x16, /* front fan 5 speed(rpm) */ + 0x17, /* front fan 6 speed(rpm) */ + 0x22, /* rear fan 1 speed(rpm) */ + 0x23, /* rear fan 2 speed(rpm) */ + 0x24, /* rear fan 3 speed(rpm) */ + 0x25, /* rear fan 4 speed(rpm) */ + 0x26, /* rear fan 5 speed(rpm) */ + 0x27, /* rear fan 6 speed(rpm) */ +}; + +/* Each client has this additional data */ +struct as7326_56x_fan_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[ARRAY_SIZE(fan_reg)]; /* Register value */ + u8 enable; + int system_temp; /*In unit of mini-Celsius*/ + int sensors_found; +}; + +enum fan_id { + FAN1_ID, + FAN2_ID, + FAN3_ID, + FAN4_ID, + FAN5_ID, + FAN6_ID +}; + +enum sysfs_fan_attributes { + FAN_PRESENT_REG, + FAN_DIRECTION_REG, + FAN_DUTY_CYCLE_PERCENTAGE, /* Only one CPLD register to control duty cycle for all fans */ + FAN1_FRONT_SPEED_RPM, + FAN2_FRONT_SPEED_RPM, + FAN3_FRONT_SPEED_RPM, + FAN4_FRONT_SPEED_RPM, + FAN5_FRONT_SPEED_RPM, + FAN6_FRONT_SPEED_RPM, + FAN1_REAR_SPEED_RPM, + FAN2_REAR_SPEED_RPM, + FAN3_REAR_SPEED_RPM, + FAN4_REAR_SPEED_RPM, + FAN5_REAR_SPEED_RPM, + FAN6_REAR_SPEED_RPM, + FAN1_DIRECTION, + FAN2_DIRECTION, + FAN3_DIRECTION, + FAN4_DIRECTION, + FAN5_DIRECTION, + FAN6_DIRECTION, + FAN1_PRESENT, + FAN2_PRESENT, + FAN3_PRESENT, + FAN4_PRESENT, + FAN5_PRESENT, + FAN6_PRESENT, + FAN1_FAULT, + FAN2_FAULT, + FAN3_FAULT, + FAN4_FAULT, + FAN5_FAULT, + FAN6_FAULT +}; + +/* Define attributes + */ +#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT);\ + static SENSOR_DEVICE_ATTR(fan##index2##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT) +#define DECLARE_FAN_FAULT_ATTR(index, index2) &sensor_dev_attr_fan##index##_fault.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_fault.dev_attr.attr + +#define DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_direction, S_IRUGO, fan_show_value, NULL, FAN##index##_DIRECTION) +#define DECLARE_FAN_DIRECTION_ATTR(index) &sensor_dev_attr_fan##index##_direction.dev_attr.attr + +#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN_DUTY_CYCLE_PERCENTAGE);\ + static SENSOR_DEVICE_ATTR(pwm##index, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN_DUTY_CYCLE_PERCENTAGE);\ + static SENSOR_DEVICE_ATTR(pwm##index##_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, FAN_DUTY_CYCLE_PERCENTAGE) + +#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) &sensor_dev_attr_fan_duty_cycle_percentage.dev_attr.attr, \ + &sensor_dev_attr_pwm##index.dev_attr.attr, \ + &sensor_dev_attr_pwm##index##_enable.dev_attr.attr + +#define DECLARE_FAN_SYSTEM_TEMP_SENSOR_DEV_ATTR() \ + static SENSOR_DEVICE_ATTR(sys_temp, S_IRUGO, get_sys_temp, NULL, FAN_DUTY_CYCLE_PERCENTAGE) + +#define DECLARE_FAN_SYSTEM_TEMP_ATTR() &sensor_dev_attr_sys_temp.dev_attr.attr + + +#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, fan_show_value, NULL, FAN##index##_PRESENT) +#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr + +#define DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_front_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_rear_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_input, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index2##_input, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM) +#define DECLARE_FAN_SPEED_RPM_ATTR(index, index2) &sensor_dev_attr_fan##index##_front_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_rear_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_input.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_input.dev_attr.attr + +/* 6 fan fault attributes in this platform */ +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1,11); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2,12); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(3,13); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(4,14); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(5,15); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(6,16); +/* 6 fan speed(rpm) attributes in this platform */ +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1,11); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2,12); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(3,13); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(4,14); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(5,15); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(6,16); +/* 6 fan present attributes in this platform */ +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(5); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(6); +/* 6 fan direction attribute in this platform */ +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(1); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(2); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(3); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(4); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(5); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(6); +/* 1 fan duty cycle attribute in this platform */ +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(1); +/* System temperature for fancontrol */ +DECLARE_FAN_SYSTEM_TEMP_SENSOR_DEV_ATTR(); + +static struct attribute *as7326_56x_fan_attributes[] = { + /* fan related attributes */ + DECLARE_FAN_FAULT_ATTR(1,11), + DECLARE_FAN_FAULT_ATTR(2,12), + DECLARE_FAN_FAULT_ATTR(3,13), + DECLARE_FAN_FAULT_ATTR(4,14), + DECLARE_FAN_FAULT_ATTR(5,15), + DECLARE_FAN_FAULT_ATTR(6,16), + DECLARE_FAN_SPEED_RPM_ATTR(1,11), + DECLARE_FAN_SPEED_RPM_ATTR(2,12), + DECLARE_FAN_SPEED_RPM_ATTR(3,13), + DECLARE_FAN_SPEED_RPM_ATTR(4,14), + DECLARE_FAN_SPEED_RPM_ATTR(5,15), + DECLARE_FAN_SPEED_RPM_ATTR(6,16), + DECLARE_FAN_PRESENT_ATTR(1), + DECLARE_FAN_PRESENT_ATTR(2), + DECLARE_FAN_PRESENT_ATTR(3), + DECLARE_FAN_PRESENT_ATTR(4), + DECLARE_FAN_PRESENT_ATTR(5), + DECLARE_FAN_PRESENT_ATTR(6), + DECLARE_FAN_DIRECTION_ATTR(1), + DECLARE_FAN_DIRECTION_ATTR(2), + DECLARE_FAN_DIRECTION_ATTR(3), + DECLARE_FAN_DIRECTION_ATTR(4), + DECLARE_FAN_DIRECTION_ATTR(5), + DECLARE_FAN_DIRECTION_ATTR(6), + DECLARE_FAN_DUTY_CYCLE_ATTR(1), + DECLARE_FAN_SYSTEM_TEMP_ATTR(), + NULL +}; + +#define FAN_DUTY_CYCLE_REG_MASK 0xF +#define FAN_MAX_DUTY_CYCLE 100 +#define FAN_REG_VAL_TO_SPEED_RPM_STEP 100 + +static int as7326_56x_fan_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int as7326_56x_fan_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* fan utility functions + */ +static u32 reg_val_to_duty_cycle(u8 reg_val) +{ + reg_val &= FAN_DUTY_CYCLE_REG_MASK; + return ((u32)(reg_val+1) * 625 + 75)/ 100; +} + +static u8 duty_cycle_to_reg_val(u8 duty_cycle) +{ + return ((u32)duty_cycle * 100 / 625) - 1; +} + +static u32 reg_val_to_speed_rpm(u8 reg_val) +{ + return (u32)reg_val * FAN_REG_VAL_TO_SPEED_RPM_STEP; +} + +static u8 reg_val_to_direction(u8 reg_val, enum fan_id id) +{ + u8 mask = (1 << id); + + reg_val &= mask; + + return reg_val ? 1 : 0; +} +static u8 reg_val_to_is_present(u8 reg_val, enum fan_id id) +{ + u8 mask = (1 << id); + + reg_val &= mask; + + return reg_val ? 0 : 1; +} + +static u8 is_fan_fault(struct as7326_56x_fan_data *data, enum fan_id id) +{ + u8 ret = 1; + int front_fan_index = FAN1_FRONT_SPEED_RPM + id; + int rear_fan_index = FAN1_REAR_SPEED_RPM + id; + + /* Check if the speed of front or rear fan is ZERO, + */ + if (reg_val_to_speed_rpm(data->reg_val[front_fan_index]) && + reg_val_to_speed_rpm(data->reg_val[rear_fan_index])) { + ret = 0; + } + + return ret; +} + +static ssize_t set_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct as7326_56x_fan_data *data = as7326_56x_fan_update_device(dev); + int error, value; + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < 0 || value > 1) + return -EINVAL; + + data->enable = value; + if (value == 0) + { + return set_duty_cycle(dev, da, buf, FAN_MAX_DUTY_CYCLE); + } + return count; +} + + +static ssize_t get_enable(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as7326_56x_fan_data *data = as7326_56x_fan_update_device(dev); + + return sprintf(buf, "%u\n", data->enable); +} +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int error, value; + struct i2c_client *client = to_i2c_client(dev); + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < 0) + return -EINVAL; + + value = (value > FAN_MAX_DUTY_CYCLE)? FAN_MAX_DUTY_CYCLE : value; + + as7326_56x_fan_write_value(client, 0x33, 0); /* Disable fan speed watch dog */ + as7326_56x_fan_write_value(client, fan_reg[FAN_DUTY_CYCLE_PERCENTAGE], duty_cycle_to_reg_val(value)); + return count; +} + +/* Due to this struct is declared at lm75.c, it cannot be include + * under Sonic environment. I duplicate it from lm75.c. + */ +struct lm75_data { + struct i2c_client *client; + struct device *hwmon_dev; + struct thermal_zone_device *tz; + struct mutex update_lock; + u8 orig_conf; + u8 resolution; /* In bits, between 9 and 12 */ + u8 resolution_limits; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long sample_time; /* In jiffies */ + s16 temp[3]; /* Register values, + 0 = input + 1 = max + 2 = hyst */ +}; + +/*Copied from lm75.c*/ +static inline long lm75_reg_to_mc(s16 temp, u8 resolution) +{ + return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); +} + +/*Get hwmon_dev from i2c_client, set hwmon_dev = NULL is failed.*/ +static struct device * get_hwmon_dev( + struct i2c_client *client) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + if(data) + { + if( data->valid == 1 && data->hwmon_dev) + { + return data->hwmon_dev; + } + + } + return NULL; +} + +/* To find hwmon index by opening hwmon under that i2c address. + */ +static int find_hwmon_index_by_FileOpen( + int bus_nr, + unsigned short addr, + OUT int *index) +{ +#define MAX_HWMON_DEVICE (10) /* Find hwmon device in 0~10*/ + struct file *sfd; + char client_name[96]; + int i=0; + + do { + snprintf(client_name, sizeof(client_name), + "/sys/bus/i2c/devices/%d-%04x/hwmon/hwmon%d/temp1_input", + bus_nr, addr, i); + + sfd = filp_open(client_name, O_RDONLY, 0); + i++; + } while( IS_ERR(sfd) && i < MAX_HWMON_DEVICE); + + if (IS_ERR(sfd)) { + pr_err("Failed to open file(%s)#%d\r\n", client_name, __LINE__); + return -ENOENT; + } + filp_close(sfd, 0); + *index = i - 1; + return 0; + +#undef MAX_HWMON_DEVICE +} + +static int get_temp_file_path( + int bus_nr, unsigned short addr, + struct device *hwmon_dev + ,char *path, int max_len) +{ + + if(hwmon_dev && strlen(dev_name(hwmon_dev))) + { + snprintf(path, max_len, + "/sys/bus/i2c/devices/%d-%04x/hwmon/%s/temp1_input", + bus_nr, addr, dev_name(hwmon_dev)); + } + else + { + int i=0; + if(find_hwmon_index_by_FileOpen( bus_nr, addr, &i)) + { + return -EIO; + } + snprintf(path, max_len, + "/sys/bus/i2c/devices/%d-%04x/hwmon/hwmon%d/temp1_input", + bus_nr, addr, i); + } + return 0; +} + +/*File read the dev file at user space.*/ +static int read_devfile_temp1_input( + struct device *dev, + int bus_nr, + unsigned short addr, + struct device *hwmon_dev, + int *miniCelsius) +{ + struct file *sfd; + char buffer[96]; + char devfile[96]; + int rc, status; + int rdlen, value; + mm_segment_t old_fs; + + rc = 0; + get_temp_file_path(bus_nr, addr, hwmon_dev, devfile, sizeof(devfile)); + sfd = filp_open(devfile, O_RDONLY, 0); + if (IS_ERR(sfd)) { + pr_err("Failed to open file(%s)#%d\r\n", devfile, __LINE__); + return -ENOENT; + } + dev_dbg(dev, "Found device:%s\n",devfile); + + if(!(sfd->f_op) || !(sfd->f_op->read) ) { + pr_err("file %s cann't readable ?\n",devfile); + return -ENOENT; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + rdlen = sfd->f_op->read(sfd, buffer, sizeof(buffer), &sfd->f_pos); + if (rdlen == 0) { + pr_err( "File(%s) empty!\n", devfile); + rc = -EIO; + goto exit; + } + status = sscanf(buffer, "%d", &value); + if (status != 1) { + rc = -EIO; + goto exit; + } + *miniCelsius = value; + dev_dbg(dev,"found sensors: %d @i2c %d-%04x\n", value, bus_nr, addr); + +exit: + set_fs(old_fs); + filp_close(sfd, 0); + return rc; +} + +static u8 is_lm75_data_due(struct i2c_client *client) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + if (time_after(jiffies, data->last_updated + data->sample_time)) + { + return 1; + } + return 0; +} +static int get_lm75_temp(struct i2c_client *client, int *miniCelsius) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + *miniCelsius = lm75_reg_to_mc(data->temp[0], data->resolution); + + return 0; +} + +static bool lm75_addr_mached(unsigned short addr) +{ + int i; + unsigned short addrs[] = THERMAL_SENSORS_ADDRS; + + for (i = 0; i < ARRAY_SIZE(addrs); i++) + { + if( addr == addrs[i]) + return 1; + } + return 0; +} + +static int _find_lm75_device(struct device *dev, void *data) +{ + struct device_driver *driver; + struct as7326_56x_fan_data *prv = data; + char *driver_name = THERMAL_SENSORS_DRIVER; + + driver = dev->driver; + if (driver && driver->name && + strcmp(driver->name, driver_name) == 0) + { + struct i2c_client *client; + client = to_i2c_client(dev); + if (client) + { + /*cannot use "struct i2c_adapter *adap = to_i2c_adapter(dev);"*/ + struct i2c_adapter *adap = client->adapter; + int miniCelsius = 0; + + if (! lm75_addr_mached(client->addr)) + { + return 0; + } + + if (!adap) { + return -ENXIO; + } + + /* If the data is not updated, read them from devfile + to drive them updateing data from chip.*/ + if (is_lm75_data_due(client)) + { + struct device *hwmon_dev; + + hwmon_dev = get_hwmon_dev(client); + if(0 == read_devfile_temp1_input(dev, adap->nr, + client->addr, hwmon_dev, &miniCelsius)) + { + prv->system_temp += miniCelsius; + prv->sensors_found++; + } + + } + else + { + get_lm75_temp(client, &miniCelsius); + prv->system_temp += miniCelsius; + prv->sensors_found++; + + } + } + } + return 0; +} + +/*Find all lm75 devices and return sum of temperatures.*/ +static ssize_t get_sys_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + ssize_t ret = 0; + struct as7326_56x_fan_data *data = as7326_56x_fan_update_device(dev); + + data->system_temp=0; + data->sensors_found=0; + i2c_for_each_dev(data, _find_lm75_device); + if (NUM_THERMAL_SENSORS != data->sensors_found) + { + dev_dbg(dev,"only %d of %d temps are found\n", + data->sensors_found, NUM_THERMAL_SENSORS); + data->system_temp = INT_MAX; + } + ret = sprintf(buf, "%d\n",data->system_temp); + return ret; +} + +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7326_56x_fan_data *data = as7326_56x_fan_update_device(dev); + ssize_t ret = 0; + + if (data->valid) { + switch (attr->index) { + case FAN_DUTY_CYCLE_PERCENTAGE: + { + u32 duty_cycle = reg_val_to_duty_cycle(data->reg_val[FAN_DUTY_CYCLE_PERCENTAGE]); + ret = sprintf(buf, "%u\n", duty_cycle); + break; + } + case FAN1_FRONT_SPEED_RPM: + case FAN2_FRONT_SPEED_RPM: + case FAN3_FRONT_SPEED_RPM: + case FAN4_FRONT_SPEED_RPM: + case FAN5_FRONT_SPEED_RPM: + case FAN6_FRONT_SPEED_RPM: + case FAN1_REAR_SPEED_RPM: + case FAN2_REAR_SPEED_RPM: + case FAN3_REAR_SPEED_RPM: + case FAN4_REAR_SPEED_RPM: + case FAN5_REAR_SPEED_RPM: + case FAN6_REAR_SPEED_RPM: + ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index])); + break; + case FAN1_PRESENT: + case FAN2_PRESENT: + case FAN3_PRESENT: + case FAN4_PRESENT: + case FAN5_PRESENT: + case FAN6_PRESENT: + ret = sprintf(buf, "%d\n", + reg_val_to_is_present(data->reg_val[FAN_PRESENT_REG], + attr->index - FAN1_PRESENT)); + break; + case FAN1_FAULT: + case FAN2_FAULT: + case FAN3_FAULT: + case FAN4_FAULT: + case FAN5_FAULT: + case FAN6_FAULT: + ret = sprintf(buf, "%d\n", is_fan_fault(data, attr->index - FAN1_FAULT)); + break; + case FAN1_DIRECTION: + case FAN2_DIRECTION: + case FAN3_DIRECTION: + case FAN4_DIRECTION: + case FAN5_DIRECTION: + case FAN6_DIRECTION: + ret = sprintf(buf, "%d\n", + reg_val_to_direction(data->reg_val[FAN_DIRECTION_REG], + attr->index - FAN1_DIRECTION)); + break; + default: + break; + } + } + + return ret; +} + +static const struct attribute_group as7326_56x_fan_group = { + .attrs = as7326_56x_fan_attributes, +}; + +static struct as7326_56x_fan_data *as7326_56x_fan_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7326_56x_fan_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || + !data->valid) { + int i; + + dev_dbg(&client->dev, "Starting as7326_56x_fan update\n"); + data->valid = 0; + + /* Update fan data + */ + for (i = 0; i < ARRAY_SIZE(data->reg_val); i++) { + int status = as7326_56x_fan_read_value(client, fan_reg[i]); + + if (status < 0) { + data->valid = 0; + mutex_unlock(&data->update_lock); + dev_dbg(&client->dev, "reg %d, err %d\n", fan_reg[i], status); + return data; + } + else { + data->reg_val[i] = status; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int as7326_56x_fan_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7326_56x_fan_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7326_56x_fan_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->enable = 0; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7326_56x_fan_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: fan '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7326_56x_fan_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7326_56x_fan_remove(struct i2c_client *client) +{ + struct as7326_56x_fan_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7326_56x_fan_group); + + return 0; +} + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x66, I2C_CLIENT_END }; + +static const struct i2c_device_id as7326_56x_fan_id[] = { + { "as7326_56x_fan", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7326_56x_fan_id); + +static struct i2c_driver as7326_56x_fan_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .probe = as7326_56x_fan_probe, + .remove = as7326_56x_fan_remove, + .id_table = as7326_56x_fan_id, + .address_list = normal_i2c, +}; + +static int __init as7326_56x_fan_init(void) +{ + return i2c_add_driver(&as7326_56x_fan_driver); +} + +static void __exit as7326_56x_fan_exit(void) +{ + i2c_del_driver(&as7326_56x_fan_driver); +} + +module_init(as7326_56x_fan_init); +module_exit(as7326_56x_fan_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7326_56x_fan driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/accton_as7326_56x_leds.c b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/accton_as7326_56x_leds.c new file mode 100644 index 000000000000..4cb7f1fa25a5 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/accton_as7326_56x_leds.c @@ -0,0 +1,438 @@ +/* + * A LED driver for the accton_as7326_56x_led + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * 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. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern int as7326_56x_cpld_read (unsigned short cpld_addr, u8 reg); +extern int as7326_56x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +extern void led_classdev_unregister(struct led_classdev *led_cdev); +extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev); +extern void led_classdev_resume(struct led_classdev *led_cdev); +extern void led_classdev_suspend(struct led_classdev *led_cdev); + +#define DRVNAME "accton_as7326_56x_led" + +struct accton_as7326_56x_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[1]; /* only 1 register*/ +}; + +static struct accton_as7326_56x_led_data *ledctl = NULL; + +/* LED related data + */ + +#define LED_CNTRLER_I2C_ADDRESS (0x60) + +#define LED_TYPE_DIAG_REG_MASK (0x3) +#define LED_MODE_DIAG_GREEN_VALUE (0x02) +#define LED_MODE_DIAG_RED_VALUE (0x01) +#define LED_MODE_DIAG_AMBER_VALUE (0x00) /*It's yellow actually. Green+Red=Yellow*/ +#define LED_MODE_DIAG_OFF_VALUE (0x03) + + +#define LED_TYPE_LOC_REG_MASK (0x80) +#define LED_MODE_LOC_ON_VALUE (0) +#define LED_MODE_LOC_OFF_VALUE (0x80) + +enum led_type { + LED_TYPE_DIAG, + LED_TYPE_LOC, + LED_TYPE_FAN, + LED_TYPE_PSU1, + LED_TYPE_PSU2 +}; + +struct led_reg { + u32 types; + u8 reg_addr; +}; + +static const struct led_reg led_reg_map[] = { + {(1<update_lock); + + if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) + || !ledctl->valid) { + int i; + + dev_dbg(&ledctl->pdev->dev, "Starting accton_as7326_56x_led update\n"); + + /* Update LED data + */ + for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { + int status = accton_as7326_56x_led_read_value(led_reg_map[i].reg_addr); + + if (status < 0) { + ledctl->valid = 0; + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg_map[i].reg_addr, status); + goto exit; + } + else + { + ledctl->reg_val[i] = status; + } + } + + ledctl->last_updated = jiffies; + ledctl->valid = 1; + } + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void accton_as7326_56x_led_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode, + enum led_type type) +{ + int reg_val; + u8 reg ; + mutex_lock(&ledctl->update_lock); + + if( !accton_getLedReg(type, ®)) + { + dev_dbg(&ledctl->pdev->dev, "Not match item for %d.\n", type); + } + + reg_val = accton_as7326_56x_led_read_value(reg); + + if (reg_val < 0) { + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", reg, reg_val); + goto exit; + } + reg_val = led_light_mode_to_reg_val(type, led_light_mode, reg_val); + accton_as7326_56x_led_write_value(reg, reg_val); + + /* to prevent the slow-update issue */ + ledctl->valid = 0; + +exit: + mutex_unlock(&ledctl->update_lock); +} + + +static void accton_as7326_56x_led_diag_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as7326_56x_led_set(led_cdev, led_light_mode, LED_TYPE_DIAG); +} + +static enum led_brightness accton_as7326_56x_led_diag_get(struct led_classdev *cdev) +{ + accton_as7326_56x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); +} + +static void accton_as7326_56x_led_loc_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as7326_56x_led_set(led_cdev, led_light_mode, LED_TYPE_LOC); +} + +static enum led_brightness accton_as7326_56x_led_loc_get(struct led_classdev *cdev) +{ + accton_as7326_56x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); +} + +static void accton_as7326_56x_led_auto_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ +} + +static enum led_brightness accton_as7326_56x_led_auto_get(struct led_classdev *cdev) +{ + return LED_MODE_AUTO; +} + +static struct led_classdev accton_as7326_56x_leds[] = { + [LED_TYPE_DIAG] = { + .name = "accton_as7326_56x_led::diag", + .default_trigger = "unused", + .brightness_set = accton_as7326_56x_led_diag_set, + .brightness_get = accton_as7326_56x_led_diag_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_RED, + }, + [LED_TYPE_LOC] = { + .name = "accton_as7326_56x_led::loc", + .default_trigger = "unused", + .brightness_set = accton_as7326_56x_led_loc_set, + .brightness_get = accton_as7326_56x_led_loc_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_BLUE, + }, + [LED_TYPE_FAN] = { + .name = "accton_as7326_56x_led::fan", + .default_trigger = "unused", + .brightness_set = accton_as7326_56x_led_auto_set, + .brightness_get = accton_as7326_56x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU1] = { + .name = "accton_as7326_56x_led::psu1", + .default_trigger = "unused", + .brightness_set = accton_as7326_56x_led_auto_set, + .brightness_get = accton_as7326_56x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU2] = { + .name = "accton_as7326_56x_led::psu2", + .default_trigger = "unused", + .brightness_set = accton_as7326_56x_led_auto_set, + .brightness_get = accton_as7326_56x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, +}; + +static int accton_as7326_56x_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as7326_56x_leds); i++) { + led_classdev_suspend(&accton_as7326_56x_leds[i]); + } + + return 0; +} + +static int accton_as7326_56x_led_resume(struct platform_device *dev) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as7326_56x_leds); i++) { + led_classdev_resume(&accton_as7326_56x_leds[i]); + } + + return 0; +} + +static int accton_as7326_56x_led_probe(struct platform_device *pdev) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(accton_as7326_56x_leds); i++) { + ret = led_classdev_register(&pdev->dev, &accton_as7326_56x_leds[i]); + + if (ret < 0) + break; + } + + /* Check if all LEDs were successfully registered */ + if (i != ARRAY_SIZE(accton_as7326_56x_leds)) { + int j; + + /* only unregister the LEDs that were successfully registered */ + for (j = 0; j < i; j++) { + led_classdev_unregister(&accton_as7326_56x_leds[i]); + } + } + + return ret; +} + +static int accton_as7326_56x_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(accton_as7326_56x_leds); i++) { + led_classdev_unregister(&accton_as7326_56x_leds[i]); + } + + return 0; +} + +static struct platform_driver accton_as7326_56x_led_driver = { + .probe = accton_as7326_56x_led_probe, + .remove = accton_as7326_56x_led_remove, + .suspend = accton_as7326_56x_led_suspend, + .resume = accton_as7326_56x_led_resume, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init accton_as7326_56x_led_init(void) +{ + int ret; + + ret = platform_driver_register(&accton_as7326_56x_led_driver); + if (ret < 0) { + goto exit; + } + + ledctl = kzalloc(sizeof(struct accton_as7326_56x_led_data), GFP_KERNEL); + if (!ledctl) { + ret = -ENOMEM; + platform_driver_unregister(&accton_as7326_56x_led_driver); + goto exit; + } + + mutex_init(&ledctl->update_lock); + + ledctl->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(ledctl->pdev)) { + ret = PTR_ERR(ledctl->pdev); + platform_driver_unregister(&accton_as7326_56x_led_driver); + kfree(ledctl); + goto exit; + } + +exit: + return ret; +} + +static void __exit accton_as7326_56x_led_exit(void) +{ + platform_device_unregister(ledctl->pdev); + platform_driver_unregister(&accton_as7326_56x_led_driver); + kfree(ledctl); +} + +module_init(accton_as7326_56x_led_init); +module_exit(accton_as7326_56x_led_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_as7326_56x_led driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/accton_as7326_56x_psu.c b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/accton_as7326_56x_psu.c new file mode 100644 index 000000000000..8c2ece6200fe --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/accton_as7326_56x_psu.c @@ -0,0 +1,277 @@ +/* + * An hwmon driver for accton as7326_56x Power Module + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, char *buf); +static int as7326_56x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); +extern int as7326_56x_cpld_read(unsigned short cpld_addr, u8 reg); + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x50, 0x53, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as7326_56x_psu_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + u8 status; /* Status(present/power_good) register read from CPLD */ + char model_name[9]; /* Model name, read from eeprom */ +}; + +static struct as7326_56x_psu_data *as7326_56x_psu_update_device(struct device *dev); + +enum as7326_56x_psu_sysfs_attributes { + PSU_PRESENT, + PSU_MODEL_NAME, + PSU_POWER_GOOD +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_status, NULL, PSU_PRESENT); +static SENSOR_DEVICE_ATTR(psu_model_name, S_IRUGO, show_model_name,NULL, PSU_MODEL_NAME); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD); + +static struct attribute *as7326_56x_psu_attributes[] = { + &sensor_dev_attr_psu_present.dev_attr.attr, + &sensor_dev_attr_psu_model_name.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + NULL +}; + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7326_56x_psu_data *data = as7326_56x_psu_update_device(dev); + u8 status = 0; + + if (attr->index == PSU_PRESENT) { + status = !(data->status >> (1-data->index) & 0x1); + } + else { /* PSU_POWER_GOOD */ + status = (data->status >> (3-data->index) & 0x1); + } + + return sprintf(buf, "%d\n", status); +} + +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as7326_56x_psu_data *data = as7326_56x_psu_update_device(dev); + + return sprintf(buf, "%s\n", data->model_name); +} + +static const struct attribute_group as7326_56x_psu_group = { + .attrs = as7326_56x_psu_attributes, +}; + +static int as7326_56x_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7326_56x_psu_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7326_56x_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7326_56x_psu_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7326_56x_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7326_56x_psu_remove(struct i2c_client *client) +{ + struct as7326_56x_psu_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7326_56x_psu_group); + kfree(data); + + return 0; +} + +enum psu_index +{ + as7326_56x_psu1, + as7326_56x_psu2 +}; + +static const struct i2c_device_id as7326_56x_psu_id[] = { + { "as7326_56x_psu1", as7326_56x_psu1 }, + { "as7326_56x_psu2", as7326_56x_psu2 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7326_56x_psu_id); + +static struct i2c_driver as7326_56x_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7326_56x_psu", + }, + .probe = as7326_56x_psu_probe, + .remove = as7326_56x_psu_remove, + .id_table = as7326_56x_psu_id, + .address_list = normal_i2c, +}; + +static int as7326_56x_psu_read_block(struct i2c_client *client, u8 command, u8 *data, + int data_len) +{ + int result = 0; + int retry_count = 5; + + while (retry_count) { + retry_count--; + + result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); + + if (unlikely(result < 0)) { + msleep(10); + continue; + } + + if (unlikely(result != data_len)) { + result = -EIO; + msleep(10); + continue; + } + + result = 0; + break; + } + + return result; +} + +static struct as7326_56x_psu_data *as7326_56x_psu_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7326_56x_psu_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int status; + int power_good = 0; + + dev_dbg(&client->dev, "Starting as7326_56x update\n"); + + /* Read psu status */ + status = as7326_56x_cpld_read(0x60, 0x2); + + if (status < 0) { + dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status); + } + else { + data->status = status; + } + + /* Read model name */ + memset(data->model_name, 0, sizeof(data->model_name)); + power_good = (data->status >> (3-data->index) & 0x1); + + if (power_good) { + status = as7326_56x_psu_read_block(client, 0x20, data->model_name, + ARRAY_SIZE(data->model_name)-1); + + if (status < 0) { + data->model_name[0] = '\0'; + dev_dbg(&client->dev, "unable to read model name from (0x%x)\n", client->addr); + } + else { + data->model_name[ARRAY_SIZE(data->model_name)-1] = '\0'; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +module_i2c_driver(as7326_56x_psu_driver); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7326_56x_psu driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/accton_i2c_cpld.c b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/accton_i2c_cpld.c new file mode 100644 index 000000000000..2274c4a934c0 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/accton_i2c_cpld.c @@ -0,0 +1,1080 @@ +/* + * Copyright (C) Brandon Chuang + * + * This module supports the accton cpld that hold the channel select + * mechanism for other i2c slave devices, such as SFP. + * This includes the: + * Accton as7326_56x CPLD1/CPLD2/CPLD3 + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I2C_RW_RETRY_COUNT 10 +#define I2C_RW_RETRY_INTERVAL 60 /* ms */ + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +enum cpld_type { + as7326_56x_cpld1, + as7326_56x_cpld2, + as7326_56x_cpld3 +}; + +struct as7326_56x_cpld_data { + enum cpld_type type; + struct device *hwmon_dev; + struct mutex update_lock; +}; + +static const struct i2c_device_id as7326_56x_cpld_id[] = { + { "as7326_56x_cpld1", as7326_56x_cpld1 }, + { "as7326_56x_cpld2", as7326_56x_cpld2 }, + { "as7326_56x_cpld3", as7326_56x_cpld3 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, as7326_56x_cpld_id); + +#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index +#define TRANSCEIVER_TXDISABLE_ATTR_ID(index) MODULE_TXDISABLE_##index +#define TRANSCEIVER_RXLOS_ATTR_ID(index) MODULE_RXLOS_##index +#define TRANSCEIVER_TXFAULT_ATTR_ID(index) MODULE_TXFAULT_##index +#define TRANSCEIVER_RESET_ATTR_ID(index) MODULE_RESET_##index + +enum as7326_56x_cpld_sysfs_attributes { + CPLD_VERSION, + ACCESS, + MODULE_PRESENT_ALL, + MODULE_RXLOS_ALL, + /* transceiver attributes */ + TRANSCEIVER_PRESENT_ATTR_ID(1), + TRANSCEIVER_PRESENT_ATTR_ID(2), + TRANSCEIVER_PRESENT_ATTR_ID(3), + TRANSCEIVER_PRESENT_ATTR_ID(4), + TRANSCEIVER_PRESENT_ATTR_ID(5), + TRANSCEIVER_PRESENT_ATTR_ID(6), + TRANSCEIVER_PRESENT_ATTR_ID(7), + TRANSCEIVER_PRESENT_ATTR_ID(8), + TRANSCEIVER_PRESENT_ATTR_ID(9), + TRANSCEIVER_PRESENT_ATTR_ID(10), + TRANSCEIVER_PRESENT_ATTR_ID(11), + TRANSCEIVER_PRESENT_ATTR_ID(12), + TRANSCEIVER_PRESENT_ATTR_ID(13), + TRANSCEIVER_PRESENT_ATTR_ID(14), + TRANSCEIVER_PRESENT_ATTR_ID(15), + TRANSCEIVER_PRESENT_ATTR_ID(16), + TRANSCEIVER_PRESENT_ATTR_ID(17), + TRANSCEIVER_PRESENT_ATTR_ID(18), + TRANSCEIVER_PRESENT_ATTR_ID(19), + TRANSCEIVER_PRESENT_ATTR_ID(20), + TRANSCEIVER_PRESENT_ATTR_ID(21), + TRANSCEIVER_PRESENT_ATTR_ID(22), + TRANSCEIVER_PRESENT_ATTR_ID(23), + TRANSCEIVER_PRESENT_ATTR_ID(24), + TRANSCEIVER_PRESENT_ATTR_ID(25), + TRANSCEIVER_PRESENT_ATTR_ID(26), + TRANSCEIVER_PRESENT_ATTR_ID(27), + TRANSCEIVER_PRESENT_ATTR_ID(28), + TRANSCEIVER_PRESENT_ATTR_ID(29), + TRANSCEIVER_PRESENT_ATTR_ID(30), + TRANSCEIVER_PRESENT_ATTR_ID(31), + TRANSCEIVER_PRESENT_ATTR_ID(32), + TRANSCEIVER_PRESENT_ATTR_ID(33), + TRANSCEIVER_PRESENT_ATTR_ID(34), + TRANSCEIVER_PRESENT_ATTR_ID(35), + TRANSCEIVER_PRESENT_ATTR_ID(36), + TRANSCEIVER_PRESENT_ATTR_ID(37), + TRANSCEIVER_PRESENT_ATTR_ID(38), + TRANSCEIVER_PRESENT_ATTR_ID(39), + TRANSCEIVER_PRESENT_ATTR_ID(40), + TRANSCEIVER_PRESENT_ATTR_ID(41), + TRANSCEIVER_PRESENT_ATTR_ID(42), + TRANSCEIVER_PRESENT_ATTR_ID(43), + TRANSCEIVER_PRESENT_ATTR_ID(44), + TRANSCEIVER_PRESENT_ATTR_ID(45), + TRANSCEIVER_PRESENT_ATTR_ID(46), + TRANSCEIVER_PRESENT_ATTR_ID(47), + TRANSCEIVER_PRESENT_ATTR_ID(48), + TRANSCEIVER_PRESENT_ATTR_ID(49), + TRANSCEIVER_PRESENT_ATTR_ID(50), + TRANSCEIVER_PRESENT_ATTR_ID(51), + TRANSCEIVER_PRESENT_ATTR_ID(52), + TRANSCEIVER_PRESENT_ATTR_ID(53), + TRANSCEIVER_PRESENT_ATTR_ID(54), + TRANSCEIVER_PRESENT_ATTR_ID(55), + TRANSCEIVER_PRESENT_ATTR_ID(56), + TRANSCEIVER_PRESENT_ATTR_ID(57), + TRANSCEIVER_PRESENT_ATTR_ID(58), + TRANSCEIVER_TXDISABLE_ATTR_ID(1), + TRANSCEIVER_TXDISABLE_ATTR_ID(2), + TRANSCEIVER_TXDISABLE_ATTR_ID(3), + TRANSCEIVER_TXDISABLE_ATTR_ID(4), + TRANSCEIVER_TXDISABLE_ATTR_ID(5), + TRANSCEIVER_TXDISABLE_ATTR_ID(6), + TRANSCEIVER_TXDISABLE_ATTR_ID(7), + TRANSCEIVER_TXDISABLE_ATTR_ID(8), + TRANSCEIVER_TXDISABLE_ATTR_ID(9), + TRANSCEIVER_TXDISABLE_ATTR_ID(10), + TRANSCEIVER_TXDISABLE_ATTR_ID(11), + TRANSCEIVER_TXDISABLE_ATTR_ID(12), + TRANSCEIVER_TXDISABLE_ATTR_ID(13), + TRANSCEIVER_TXDISABLE_ATTR_ID(14), + TRANSCEIVER_TXDISABLE_ATTR_ID(15), + TRANSCEIVER_TXDISABLE_ATTR_ID(16), + TRANSCEIVER_TXDISABLE_ATTR_ID(17), + TRANSCEIVER_TXDISABLE_ATTR_ID(18), + TRANSCEIVER_TXDISABLE_ATTR_ID(19), + TRANSCEIVER_TXDISABLE_ATTR_ID(20), + TRANSCEIVER_TXDISABLE_ATTR_ID(21), + TRANSCEIVER_TXDISABLE_ATTR_ID(22), + TRANSCEIVER_TXDISABLE_ATTR_ID(23), + TRANSCEIVER_TXDISABLE_ATTR_ID(24), + TRANSCEIVER_TXDISABLE_ATTR_ID(25), + TRANSCEIVER_TXDISABLE_ATTR_ID(26), + TRANSCEIVER_TXDISABLE_ATTR_ID(27), + TRANSCEIVER_TXDISABLE_ATTR_ID(28), + TRANSCEIVER_TXDISABLE_ATTR_ID(29), + TRANSCEIVER_TXDISABLE_ATTR_ID(30), + TRANSCEIVER_TXDISABLE_ATTR_ID(31), + TRANSCEIVER_TXDISABLE_ATTR_ID(32), + TRANSCEIVER_TXDISABLE_ATTR_ID(33), + TRANSCEIVER_TXDISABLE_ATTR_ID(34), + TRANSCEIVER_TXDISABLE_ATTR_ID(35), + TRANSCEIVER_TXDISABLE_ATTR_ID(36), + TRANSCEIVER_TXDISABLE_ATTR_ID(37), + TRANSCEIVER_TXDISABLE_ATTR_ID(38), + TRANSCEIVER_TXDISABLE_ATTR_ID(39), + TRANSCEIVER_TXDISABLE_ATTR_ID(40), + TRANSCEIVER_TXDISABLE_ATTR_ID(41), + TRANSCEIVER_TXDISABLE_ATTR_ID(42), + TRANSCEIVER_TXDISABLE_ATTR_ID(43), + TRANSCEIVER_TXDISABLE_ATTR_ID(44), + TRANSCEIVER_TXDISABLE_ATTR_ID(45), + TRANSCEIVER_TXDISABLE_ATTR_ID(46), + TRANSCEIVER_TXDISABLE_ATTR_ID(47), + TRANSCEIVER_TXDISABLE_ATTR_ID(48), + TRANSCEIVER_TXDISABLE_ATTR_ID(57), + TRANSCEIVER_TXDISABLE_ATTR_ID(58), + TRANSCEIVER_RXLOS_ATTR_ID(1), + TRANSCEIVER_RXLOS_ATTR_ID(2), + TRANSCEIVER_RXLOS_ATTR_ID(3), + TRANSCEIVER_RXLOS_ATTR_ID(4), + TRANSCEIVER_RXLOS_ATTR_ID(5), + TRANSCEIVER_RXLOS_ATTR_ID(6), + TRANSCEIVER_RXLOS_ATTR_ID(7), + TRANSCEIVER_RXLOS_ATTR_ID(8), + TRANSCEIVER_RXLOS_ATTR_ID(9), + TRANSCEIVER_RXLOS_ATTR_ID(10), + TRANSCEIVER_RXLOS_ATTR_ID(11), + TRANSCEIVER_RXLOS_ATTR_ID(12), + TRANSCEIVER_RXLOS_ATTR_ID(13), + TRANSCEIVER_RXLOS_ATTR_ID(14), + TRANSCEIVER_RXLOS_ATTR_ID(15), + TRANSCEIVER_RXLOS_ATTR_ID(16), + TRANSCEIVER_RXLOS_ATTR_ID(17), + TRANSCEIVER_RXLOS_ATTR_ID(18), + TRANSCEIVER_RXLOS_ATTR_ID(19), + TRANSCEIVER_RXLOS_ATTR_ID(20), + TRANSCEIVER_RXLOS_ATTR_ID(21), + TRANSCEIVER_RXLOS_ATTR_ID(22), + TRANSCEIVER_RXLOS_ATTR_ID(23), + TRANSCEIVER_RXLOS_ATTR_ID(24), + TRANSCEIVER_RXLOS_ATTR_ID(25), + TRANSCEIVER_RXLOS_ATTR_ID(26), + TRANSCEIVER_RXLOS_ATTR_ID(27), + TRANSCEIVER_RXLOS_ATTR_ID(28), + TRANSCEIVER_RXLOS_ATTR_ID(29), + TRANSCEIVER_RXLOS_ATTR_ID(30), + TRANSCEIVER_RXLOS_ATTR_ID(31), + TRANSCEIVER_RXLOS_ATTR_ID(32), + TRANSCEIVER_RXLOS_ATTR_ID(33), + TRANSCEIVER_RXLOS_ATTR_ID(34), + TRANSCEIVER_RXLOS_ATTR_ID(35), + TRANSCEIVER_RXLOS_ATTR_ID(36), + TRANSCEIVER_RXLOS_ATTR_ID(37), + TRANSCEIVER_RXLOS_ATTR_ID(38), + TRANSCEIVER_RXLOS_ATTR_ID(39), + TRANSCEIVER_RXLOS_ATTR_ID(40), + TRANSCEIVER_RXLOS_ATTR_ID(41), + TRANSCEIVER_RXLOS_ATTR_ID(42), + TRANSCEIVER_RXLOS_ATTR_ID(43), + TRANSCEIVER_RXLOS_ATTR_ID(44), + TRANSCEIVER_RXLOS_ATTR_ID(45), + TRANSCEIVER_RXLOS_ATTR_ID(46), + TRANSCEIVER_RXLOS_ATTR_ID(47), + TRANSCEIVER_RXLOS_ATTR_ID(48), + TRANSCEIVER_RXLOS_ATTR_ID(57), + TRANSCEIVER_RXLOS_ATTR_ID(58), + TRANSCEIVER_TXFAULT_ATTR_ID(1), + TRANSCEIVER_TXFAULT_ATTR_ID(2), + TRANSCEIVER_TXFAULT_ATTR_ID(3), + TRANSCEIVER_TXFAULT_ATTR_ID(4), + TRANSCEIVER_TXFAULT_ATTR_ID(5), + TRANSCEIVER_TXFAULT_ATTR_ID(6), + TRANSCEIVER_TXFAULT_ATTR_ID(7), + TRANSCEIVER_TXFAULT_ATTR_ID(8), + TRANSCEIVER_TXFAULT_ATTR_ID(9), + TRANSCEIVER_TXFAULT_ATTR_ID(10), + TRANSCEIVER_TXFAULT_ATTR_ID(11), + TRANSCEIVER_TXFAULT_ATTR_ID(12), + TRANSCEIVER_TXFAULT_ATTR_ID(13), + TRANSCEIVER_TXFAULT_ATTR_ID(14), + TRANSCEIVER_TXFAULT_ATTR_ID(15), + TRANSCEIVER_TXFAULT_ATTR_ID(16), + TRANSCEIVER_TXFAULT_ATTR_ID(17), + TRANSCEIVER_TXFAULT_ATTR_ID(18), + TRANSCEIVER_TXFAULT_ATTR_ID(19), + TRANSCEIVER_TXFAULT_ATTR_ID(20), + TRANSCEIVER_TXFAULT_ATTR_ID(21), + TRANSCEIVER_TXFAULT_ATTR_ID(22), + TRANSCEIVER_TXFAULT_ATTR_ID(23), + TRANSCEIVER_TXFAULT_ATTR_ID(24), + TRANSCEIVER_TXFAULT_ATTR_ID(25), + TRANSCEIVER_TXFAULT_ATTR_ID(26), + TRANSCEIVER_TXFAULT_ATTR_ID(27), + TRANSCEIVER_TXFAULT_ATTR_ID(28), + TRANSCEIVER_TXFAULT_ATTR_ID(29), + TRANSCEIVER_TXFAULT_ATTR_ID(30), + TRANSCEIVER_TXFAULT_ATTR_ID(31), + TRANSCEIVER_TXFAULT_ATTR_ID(32), + TRANSCEIVER_TXFAULT_ATTR_ID(33), + TRANSCEIVER_TXFAULT_ATTR_ID(34), + TRANSCEIVER_TXFAULT_ATTR_ID(35), + TRANSCEIVER_TXFAULT_ATTR_ID(36), + TRANSCEIVER_TXFAULT_ATTR_ID(37), + TRANSCEIVER_TXFAULT_ATTR_ID(38), + TRANSCEIVER_TXFAULT_ATTR_ID(39), + TRANSCEIVER_TXFAULT_ATTR_ID(40), + TRANSCEIVER_TXFAULT_ATTR_ID(41), + TRANSCEIVER_TXFAULT_ATTR_ID(42), + TRANSCEIVER_TXFAULT_ATTR_ID(43), + TRANSCEIVER_TXFAULT_ATTR_ID(44), + TRANSCEIVER_TXFAULT_ATTR_ID(45), + TRANSCEIVER_TXFAULT_ATTR_ID(46), + TRANSCEIVER_TXFAULT_ATTR_ID(47), + TRANSCEIVER_TXFAULT_ATTR_ID(48), + TRANSCEIVER_TXFAULT_ATTR_ID(57), + TRANSCEIVER_TXFAULT_ATTR_ID(58), +}; + +/* sysfs attributes for hwmon + */ +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_present_all(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_rxlos_all(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t show_version(struct device *dev, struct device_attribute *da, + char *buf); +static int as7326_56x_cpld_read_internal(struct i2c_client *client, u8 reg); +static int as7326_56x_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value); + +/* transceiver attributes */ +#define DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, show_status, NULL, MODULE_PRESENT_##index) +#define DECLARE_TRANSCEIVER_PRESENT_ATTR(index) &sensor_dev_attr_module_present_##index.dev_attr.attr + +#define DECLARE_TRANSCEIVER_RESET_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_reset_##index, S_IRUGO | S_IWUSR, show_status, set_reset, MODULE_RESET_##index) +#define DECLARE_TRANSCEIVER_RESET_ATTR(index) &sensor_dev_attr_module_reset_##index.dev_attr.attr + +#define DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_tx_disable_##index, S_IRUGO | S_IWUSR, show_status, set_tx_disable, MODULE_TXDISABLE_##index); \ + static SENSOR_DEVICE_ATTR(module_rx_los_##index, S_IRUGO, show_status, NULL, MODULE_RXLOS_##index); \ + static SENSOR_DEVICE_ATTR(module_tx_fault_##index, S_IRUGO, show_status, NULL, MODULE_TXFAULT_##index) +#define DECLARE_SFP_TRANSCEIVER_ATTR(index) \ + &sensor_dev_attr_module_tx_disable_##index.dev_attr.attr, \ + &sensor_dev_attr_module_rx_los_##index.dev_attr.attr, \ + &sensor_dev_attr_module_tx_fault_##index.dev_attr.attr + +static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_version, NULL, CPLD_VERSION); +static SENSOR_DEVICE_ATTR(access, S_IWUSR, NULL, access, ACCESS); +/* transceiver attributes */ +static SENSOR_DEVICE_ATTR(module_present_all, S_IRUGO, show_present_all, NULL, MODULE_PRESENT_ALL); +static SENSOR_DEVICE_ATTR(module_rx_los_all, S_IRUGO, show_rxlos_all, NULL, MODULE_RXLOS_ALL); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(1); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(2); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(3); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(4); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(5); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(6); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(7); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(8); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(9); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(10); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(11); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(12); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(13); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(14); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(15); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(16); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(17); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(18); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(19); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(20); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(21); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(22); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(23); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(24); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(25); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(26); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(27); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(28); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(29); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(30); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(31); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(32); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(33); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(34); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(35); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(36); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(37); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(38); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(39); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(40); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(41); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(42); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(43); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(44); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(45); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(46); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(47); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(48); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(49); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(50); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(51); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(52); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(53); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(54); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(55); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(56); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(57); +DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(58); + +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(1); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(2); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(3); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(4); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(5); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(6); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(7); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(8); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(9); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(10); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(11); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(12); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(13); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(14); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(15); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(16); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(17); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(18); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(19); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(20); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(21); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(22); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(23); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(24); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(25); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(26); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(27); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(28); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(29); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(30); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(31); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(32); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(33); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(34); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(35); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(36); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(37); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(38); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(39); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(40); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(41); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(42); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(43); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(44); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(45); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(46); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(47); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(48); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(57); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(58); + +static struct attribute *as7326_56x_cpld3_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + NULL +}; + +static const struct attribute_group as7326_56x_cpld3_group = { + .attrs = as7326_56x_cpld3_attributes, +}; + +static struct attribute *as7326_56x_cpld2_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + /* transceiver attributes */ + &sensor_dev_attr_module_present_all.dev_attr.attr, + &sensor_dev_attr_module_rx_los_all.dev_attr.attr, + DECLARE_TRANSCEIVER_PRESENT_ATTR(1), + DECLARE_TRANSCEIVER_PRESENT_ATTR(2), + DECLARE_TRANSCEIVER_PRESENT_ATTR(3), + DECLARE_TRANSCEIVER_PRESENT_ATTR(4), + DECLARE_TRANSCEIVER_PRESENT_ATTR(5), + DECLARE_TRANSCEIVER_PRESENT_ATTR(6), + DECLARE_TRANSCEIVER_PRESENT_ATTR(7), + DECLARE_TRANSCEIVER_PRESENT_ATTR(8), + DECLARE_TRANSCEIVER_PRESENT_ATTR(9), + DECLARE_TRANSCEIVER_PRESENT_ATTR(10), + DECLARE_TRANSCEIVER_PRESENT_ATTR(11), + DECLARE_TRANSCEIVER_PRESENT_ATTR(12), + DECLARE_TRANSCEIVER_PRESENT_ATTR(13), + DECLARE_TRANSCEIVER_PRESENT_ATTR(14), + DECLARE_TRANSCEIVER_PRESENT_ATTR(15), + DECLARE_TRANSCEIVER_PRESENT_ATTR(16), + DECLARE_TRANSCEIVER_PRESENT_ATTR(17), + DECLARE_TRANSCEIVER_PRESENT_ATTR(18), + DECLARE_TRANSCEIVER_PRESENT_ATTR(19), + DECLARE_TRANSCEIVER_PRESENT_ATTR(20), + DECLARE_TRANSCEIVER_PRESENT_ATTR(21), + DECLARE_TRANSCEIVER_PRESENT_ATTR(22), + DECLARE_TRANSCEIVER_PRESENT_ATTR(23), + DECLARE_TRANSCEIVER_PRESENT_ATTR(24), + DECLARE_TRANSCEIVER_PRESENT_ATTR(25), + DECLARE_TRANSCEIVER_PRESENT_ATTR(26), + DECLARE_TRANSCEIVER_PRESENT_ATTR(27), + DECLARE_TRANSCEIVER_PRESENT_ATTR(28), + DECLARE_TRANSCEIVER_PRESENT_ATTR(29), + DECLARE_TRANSCEIVER_PRESENT_ATTR(30), + DECLARE_SFP_TRANSCEIVER_ATTR(1), + DECLARE_SFP_TRANSCEIVER_ATTR(2), + DECLARE_SFP_TRANSCEIVER_ATTR(3), + DECLARE_SFP_TRANSCEIVER_ATTR(4), + DECLARE_SFP_TRANSCEIVER_ATTR(5), + DECLARE_SFP_TRANSCEIVER_ATTR(6), + DECLARE_SFP_TRANSCEIVER_ATTR(7), + DECLARE_SFP_TRANSCEIVER_ATTR(8), + DECLARE_SFP_TRANSCEIVER_ATTR(9), + DECLARE_SFP_TRANSCEIVER_ATTR(10), + DECLARE_SFP_TRANSCEIVER_ATTR(11), + DECLARE_SFP_TRANSCEIVER_ATTR(12), + DECLARE_SFP_TRANSCEIVER_ATTR(13), + DECLARE_SFP_TRANSCEIVER_ATTR(14), + DECLARE_SFP_TRANSCEIVER_ATTR(15), + DECLARE_SFP_TRANSCEIVER_ATTR(16), + DECLARE_SFP_TRANSCEIVER_ATTR(17), + DECLARE_SFP_TRANSCEIVER_ATTR(18), + DECLARE_SFP_TRANSCEIVER_ATTR(19), + DECLARE_SFP_TRANSCEIVER_ATTR(20), + DECLARE_SFP_TRANSCEIVER_ATTR(21), + DECLARE_SFP_TRANSCEIVER_ATTR(22), + DECLARE_SFP_TRANSCEIVER_ATTR(23), + DECLARE_SFP_TRANSCEIVER_ATTR(24), + DECLARE_SFP_TRANSCEIVER_ATTR(25), + DECLARE_SFP_TRANSCEIVER_ATTR(26), + DECLARE_SFP_TRANSCEIVER_ATTR(27), + DECLARE_SFP_TRANSCEIVER_ATTR(28), + DECLARE_SFP_TRANSCEIVER_ATTR(29), + DECLARE_SFP_TRANSCEIVER_ATTR(30), + NULL +}; + +static const struct attribute_group as7326_56x_cpld2_group = { + .attrs = as7326_56x_cpld2_attributes, +}; + +static struct attribute *as7326_56x_cpld1_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + /* transceiver attributes */ + &sensor_dev_attr_module_present_all.dev_attr.attr, + &sensor_dev_attr_module_rx_los_all.dev_attr.attr, + DECLARE_TRANSCEIVER_PRESENT_ATTR(31), + DECLARE_TRANSCEIVER_PRESENT_ATTR(32), + DECLARE_TRANSCEIVER_PRESENT_ATTR(33), + DECLARE_TRANSCEIVER_PRESENT_ATTR(34), + DECLARE_TRANSCEIVER_PRESENT_ATTR(35), + DECLARE_TRANSCEIVER_PRESENT_ATTR(36), + DECLARE_TRANSCEIVER_PRESENT_ATTR(37), + DECLARE_TRANSCEIVER_PRESENT_ATTR(38), + DECLARE_TRANSCEIVER_PRESENT_ATTR(39), + DECLARE_TRANSCEIVER_PRESENT_ATTR(40), + DECLARE_TRANSCEIVER_PRESENT_ATTR(41), + DECLARE_TRANSCEIVER_PRESENT_ATTR(42), + DECLARE_TRANSCEIVER_PRESENT_ATTR(43), + DECLARE_TRANSCEIVER_PRESENT_ATTR(44), + DECLARE_TRANSCEIVER_PRESENT_ATTR(45), + DECLARE_TRANSCEIVER_PRESENT_ATTR(46), + DECLARE_TRANSCEIVER_PRESENT_ATTR(47), + DECLARE_TRANSCEIVER_PRESENT_ATTR(48), + DECLARE_TRANSCEIVER_PRESENT_ATTR(49), + DECLARE_TRANSCEIVER_PRESENT_ATTR(50), + DECLARE_TRANSCEIVER_PRESENT_ATTR(51), + DECLARE_TRANSCEIVER_PRESENT_ATTR(52), + DECLARE_TRANSCEIVER_PRESENT_ATTR(53), + DECLARE_TRANSCEIVER_PRESENT_ATTR(54), + DECLARE_TRANSCEIVER_PRESENT_ATTR(55), + DECLARE_TRANSCEIVER_PRESENT_ATTR(56), + DECLARE_TRANSCEIVER_PRESENT_ATTR(57), + DECLARE_TRANSCEIVER_PRESENT_ATTR(58), + DECLARE_SFP_TRANSCEIVER_ATTR(31), + DECLARE_SFP_TRANSCEIVER_ATTR(32), + DECLARE_SFP_TRANSCEIVER_ATTR(33), + DECLARE_SFP_TRANSCEIVER_ATTR(34), + DECLARE_SFP_TRANSCEIVER_ATTR(35), + DECLARE_SFP_TRANSCEIVER_ATTR(36), + DECLARE_SFP_TRANSCEIVER_ATTR(37), + DECLARE_SFP_TRANSCEIVER_ATTR(38), + DECLARE_SFP_TRANSCEIVER_ATTR(39), + DECLARE_SFP_TRANSCEIVER_ATTR(40), + DECLARE_SFP_TRANSCEIVER_ATTR(41), + DECLARE_SFP_TRANSCEIVER_ATTR(42), + DECLARE_SFP_TRANSCEIVER_ATTR(43), + DECLARE_SFP_TRANSCEIVER_ATTR(44), + DECLARE_SFP_TRANSCEIVER_ATTR(45), + DECLARE_SFP_TRANSCEIVER_ATTR(46), + DECLARE_SFP_TRANSCEIVER_ATTR(47), + DECLARE_SFP_TRANSCEIVER_ATTR(48), + DECLARE_SFP_TRANSCEIVER_ATTR(57), + DECLARE_SFP_TRANSCEIVER_ATTR(58), + NULL +}; + +static const struct attribute_group as7326_56x_cpld1_group = { + .attrs = as7326_56x_cpld1_attributes, +}; + +static ssize_t show_present_all(struct device *dev, struct device_attribute *da, + char *buf) +{ + int i, status; + u8 values[4] = {0}; + u8 regs[] = {0x9, 0xA, 0xB, 0x18}; + struct i2c_client *client = to_i2c_client(dev); + struct as7326_56x_cpld_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + status = as7326_56x_cpld_read_internal(client, regs[i]); + + if (status < 0) { + goto exit; + } + + values[i] = ~(u8)status; + } + + mutex_unlock(&data->update_lock); + + /* Return values 1 -> 56 in order */ + if (data->type == as7326_56x_cpld2) { + values[3] &= 0xF; + } + else { /* as7326_56x_cpld3 */ + values[3] &= 0x3; + } + + return sprintf(buf, "%.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], values[3]); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_rxlos_all(struct device *dev, struct device_attribute *da, + char *buf) +{ + int i, status; + u8 values[3] = {0}; + u8 regs[] = {0x12, 0x13, 0x14}; + struct i2c_client *client = to_i2c_client(dev); + struct as7326_56x_cpld_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + status = as7326_56x_cpld_read_internal(client, regs[i]); + + if (status < 0) { + goto exit; + } + + values[i] = (u8)status; + } + + mutex_unlock(&data->update_lock); + + /* Return values 1 -> 24 in order */ + return sprintf(buf, "%.2x %.2x %.2x\n", values[0], values[1], values[2]); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7326_56x_cpld_data *data = i2c_get_clientdata(client); + int status = 0; + u8 reg = 0, mask = 0, revert = 0; + + switch (attr->index) { + case MODULE_PRESENT_1 ... MODULE_PRESENT_30: + reg = 0x0f + (attr->index-MODULE_PRESENT_1)/8; + mask = 0x1 << ((attr->index - MODULE_PRESENT_1)%8); + break; + case MODULE_PRESENT_31 ... MODULE_PRESENT_48: + reg = 0x10 + (attr->index-MODULE_PRESENT_31)/8; + mask = 0x1 << ((attr->index - MODULE_PRESENT_31)%8); + break; + case MODULE_PRESENT_57 ... MODULE_PRESENT_58: + reg = 0x12; + mask = 0x1 << (( MODULE_PRESENT_58 - attr->index)+2); + break; + case MODULE_PRESENT_49 ... MODULE_PRESENT_56: /*QSFP*/ + reg = 0x13 ; + mask = 0x1 << ((attr->index - MODULE_PRESENT_49)%8); + break; + case MODULE_TXFAULT_1 ... MODULE_TXFAULT_30: + reg = 0x03 + (attr->index - MODULE_TXFAULT_1)/8; + mask = 0x1 << ((attr->index - MODULE_TXFAULT_1)%8); + break; + case MODULE_TXFAULT_31 ... MODULE_TXFAULT_48: + reg = 0x1a + (attr->index-MODULE_TXFAULT_31)/8; + mask = 0x1 << ((attr->index - MODULE_TXFAULT_31)%8); + break; + case MODULE_TXFAULT_57 ... MODULE_TXFAULT_58: + reg = 0x1c; + mask = 0x1 << (( attr->index - MODULE_TXFAULT_57)+2); + break; + case MODULE_TXDISABLE_1 ... MODULE_TXDISABLE_30: + reg = 0x07 + (attr->index - MODULE_TXDISABLE_1)/8; + mask = 0x1 << ((attr->index - MODULE_TXDISABLE_1)%8); + break; + case MODULE_TXDISABLE_31 ... MODULE_TXDISABLE_48: + reg = 0x14 + (attr->index-MODULE_TXDISABLE_31)/8; + mask = 0x1 << ((attr->index - MODULE_TXDISABLE_31)%8); + break; + case MODULE_TXDISABLE_57 ... MODULE_TXDISABLE_58: + reg = 0x16; + mask = 0x1 << ((attr->index - MODULE_TXDISABLE_57)+2); + break; + case MODULE_RXLOS_1 ... MODULE_RXLOS_30: + reg = 0x0b + (attr->index - MODULE_RXLOS_1)/8; + mask = 0x1 << ((attr->index - MODULE_RXLOS_1)%8); + break; + case MODULE_RXLOS_31 ... MODULE_RXLOS_48: + reg = 0x17 + (attr->index-MODULE_RXLOS_31)/8; + mask = 0x1 << ((attr->index - MODULE_RXLOS_31)%8); + break; + case MODULE_RXLOS_57 ... MODULE_RXLOS_58: + reg = 0x19; + mask = 0x1 << (( attr->index - MODULE_RXLOS_57)+2); + break; + default: + return 0; + } + + if (attr->index >= MODULE_PRESENT_1 && attr->index <= MODULE_PRESENT_56) { + revert = 1; + } + + mutex_lock(&data->update_lock); + status = as7326_56x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", revert ? !(status & mask) : !!(status & mask)); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7326_56x_cpld_data *data = i2c_get_clientdata(client); + long disable; + int status; + u8 reg = 0, mask = 0; + + status = kstrtol(buf, 10, &disable); + if (status) { + return status; + } + + switch (attr->index) { + case MODULE_TXDISABLE_1 ... MODULE_TXDISABLE_30: + reg = 0x07 + (attr->index - MODULE_TXDISABLE_1)/8; + mask = 0x1 << ((attr->index - MODULE_TXDISABLE_1)%8); + break; + case MODULE_TXDISABLE_31 ... MODULE_TXDISABLE_48: + reg = 0x14 + (attr->index - MODULE_TXDISABLE_31)/8; + mask = 0x1 << ((attr->index - MODULE_TXDISABLE_31)%8); + break; + case MODULE_TXDISABLE_57 ... MODULE_TXDISABLE_58: + reg = 0x16; + mask = 0x1 << ((attr->index - MODULE_TXDISABLE_57)+2); + break; + default: + return 0; + } + + /* Read current status */ + mutex_lock(&data->update_lock); + status = as7326_56x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + + /* Update tx_disable status */ + if (disable) { + status |= mask; + } + else { + status &= ~mask; + } + + status = as7326_56x_cpld_write_internal(client, reg, status); + if (unlikely(status < 0)) { + goto exit; + } + + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int status; + u32 addr, val; + struct i2c_client *client = to_i2c_client(dev); + struct as7326_56x_cpld_data *data = i2c_get_clientdata(client); + + if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) { + return -EINVAL; + } + + if (addr > 0xFF || val > 0xFF) { + return -EINVAL; + } + + mutex_lock(&data->update_lock); + status = as7326_56x_cpld_write_internal(client, addr, val); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static void as7326_56x_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void as7326_56x_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + + mutex_unlock(&list_lock); +} + +static ssize_t show_version(struct device *dev, struct device_attribute *attr, char *buf) +{ + int val = 0; + struct i2c_client *client = to_i2c_client(dev); + + val = i2c_smbus_read_byte_data(client, 0x1); + + if (val < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x1) err %d\n", client->addr, val); + } + + return sprintf(buf, "%d", val); +} + +/* + * I2C init/probing/exit functions + */ +static int as7326_56x_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + struct as7326_56x_cpld_data *data; + int ret = -ENODEV; + const struct attribute_group *group = NULL; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) + goto exit; + + data = kzalloc(sizeof(struct as7326_56x_cpld_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->type = id->driver_data; + + /* Register sysfs hooks */ + switch (data->type) { + case as7326_56x_cpld1: + group = &as7326_56x_cpld1_group; + break; + case as7326_56x_cpld2: + group = &as7326_56x_cpld2_group; + break; + case as7326_56x_cpld3: + group = &as7326_56x_cpld3_group; + break; + default: + break; + } + + if (group) { + ret = sysfs_create_group(&client->dev.kobj, group); + if (ret) { + goto exit_free; + } + } + + as7326_56x_cpld_add_client(client); + return 0; + +exit_free: + kfree(data); +exit: + return ret; +} + +static int as7326_56x_cpld_remove(struct i2c_client *client) +{ + struct as7326_56x_cpld_data *data = i2c_get_clientdata(client); + const struct attribute_group *group = NULL; + + as7326_56x_cpld_remove_client(client); + + /* Remove sysfs hooks */ + switch (data->type) { + case as7326_56x_cpld1: + group = &as7326_56x_cpld1_group; + break; + case as7326_56x_cpld2: + group = &as7326_56x_cpld2_group; + break; + case as7326_56x_cpld3: + group = &as7326_56x_cpld3_group; + break; + default: + break; + } + + if (group) { + sysfs_remove_group(&client->dev.kobj, group); + } + + kfree(data); + + return 0; +} + +static int as7326_56x_cpld_read_internal(struct i2c_client *client, u8 reg) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, reg); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static int as7326_56x_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_write_byte_data(client, reg, value); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +int as7326_56x_cpld_read(unsigned short cpld_addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = as7326_56x_cpld_read_internal(cpld_node->client, reg); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as7326_56x_cpld_read); + +int as7326_56x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = as7326_56x_cpld_write_internal(cpld_node->client, reg, value); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as7326_56x_cpld_write); + +static struct i2c_driver as7326_56x_cpld_driver = { + .driver = { + .name = "as7326_56x_cpld", + .owner = THIS_MODULE, + }, + .probe = as7326_56x_cpld_probe, + .remove = as7326_56x_cpld_remove, + .id_table = as7326_56x_cpld_id, +}; + +static int __init as7326_56x_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&as7326_56x_cpld_driver); +} + +static void __exit as7326_56x_cpld_exit(void) +{ + i2c_del_driver(&as7326_56x_cpld_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("Accton I2C CPLD driver"); +MODULE_LICENSE("GPL"); + +module_init(as7326_56x_cpld_init); +module_exit(as7326_56x_cpld_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/ym2651y.c b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/ym2651y.c new file mode 120000 index 000000000000..f4d67640ccc3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/modules/ym2651y.c @@ -0,0 +1 @@ +../../common/modules/ym2651y.c \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/service/as7326-platform-monitor.service b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/service/as7326-platform-monitor.service new file mode 100755 index 000000000000..3af4405fb493 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/service/as7326-platform-monitor.service @@ -0,0 +1,17 @@ +[Unit] +Description=Accton AS7326-56X Platform Monitoring service +Before=pmon.service +After=sysinit.target +DefaultDependencies=no + +[Service] +ExecStartPre=/usr/local/bin/accton_as7326_util.py install +ExecStart=/usr/local/bin/accton_as7326_monitor.py +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/setup.py b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/setup.py new file mode 100755 index 000000000000..77114c71285b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/setup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env pytho + +import os +import sys +from setuptools import setup +os.listdir + +setup( + name='as7326_56x', + version='1.0', + description='Module to initialize Accton AS7326-56X platforms', + + packages=['as7326_56x'], + package_dir={'as7326_56x': 'as7326-56x/classes'}, +) + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/utils/README b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/utils/README new file mode 100755 index 000000000000..ef0d03cf5e45 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/utils/README @@ -0,0 +1,74 @@ +Copyright (C) 2016 Accton Networks, Inc. + +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 3 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, see . + +Contents of this package: + module - Contains source code of as7326 kernel driver modules. + util - operational scripts. + +Sonic creates a docker container and run building process under it. +If user tries to built new drivers, please get into that docker and +dpkg-buildpackage for them. + +All Linux kernel code is licensed under the GPLv1. All other code is +licensed under the GPLv3. Please see the LICENSE file for copies of +both licenses. + +The code for integacting with Accton AS7326-56X has 2 parts, +kernel drivers and operational script. +The kernel drivers of peripherals are under module/ directory. +1. These drivers can be built to individual ko during dpkg-buildpackage. +2. A operational script, accton_as7326_util.py, for device initializatian. + Run "accton_as7326_util.py install" to install drivers. + +To initialize the system, run "accton_as7326_util.py install". +To clean up the drivers & devices, run "accton_as7326_util.py clean". +To dump information of sensors, run "accton_as7326_util.py show". +To dump SFP EEPROM, run "accton_as7326_util.py sff". +To set fan speed, run "accton_as7326_util.py set fan". +To enable/disable SFP emission, run "accton_as7326_util.py set sfp". +To set system LEDs' color, run "accton_as7326_util.py set led" +For more information, run "accton_as7326_util.py --help". + +==================================================================== +Besides applying accton_as7326_util.py to access peripherals, you can +access peripherals by sysfs nodes directly after the installation is run. + +System LED: + There are 5 system LEDs at the lower-left corner of front panel. + They are loc, diag, fan, ps1, and ps2. + The sysfs interface color mappings are as follows: + Brightness: + 0 => off + 1 => green + 2 => amber + 3 => red + 4 => blue + But not all colors are available for each LED. + +Fan Control: + There are 10 fans inside 6 fan modules. + All fans share 1 duty setting, ranged from 0~100. + +Thermal sensers: + 3 temperature sensors are controlled by the lm75 kernel modules. + +PSUs: + There 2 power supplies slot at the left/right side of the back. + Once if a PSU is not plugged, the status of it is shown failed. + +There are 48 SFP+ and 8 QSFP modules are equipped. +Before operating on PSU and QSFP+, please make sure it is well plugged. +Otherwise, operation is going to fail. + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/utils/accton_as7326_monitor.py b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/utils/accton_as7326_monitor.py new file mode 100755 index 000000000000..856f194c5e53 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/utils/accton_as7326_monitor.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# 1/10/2018: Jostar modify for as7716_32 +# 3/23/2018: Roy Lee modify for as7326_56x +# ------------------------------------------------------------------ + +try: + import os + import sys, getopt + import subprocess + import click + import imp + import logging + import logging.config + import types + import time # this is only being used as part of the example + import traceback + from tabulate import tabulate + from as7326_56x.fanutil import FanUtil + from as7326_56x.thermalutil import ThermalUtil +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + +# Deafults +VERSION = '1.0' +FUNCTION_NAME = 'accton_as7326_monitor' + +global log_file +global log_level + +# (LM75_1+ LM75_2+ LM75_3) is LM75 at i2c addresses 0x48, 0x49, and 0x4A. +# TMP = (LM75_1+ LM75_2+ LM75_3)/3 +#1. If TMP < 35, All fans run with duty 31.25%. +#2. If TMP>=35 or the temperature of any one of fan is higher than 40, +# All fans run with duty 50% +#3. If TMP >= 40 or the temperature of any one of fan is higher than 45, +# All fans run with duty 62.5%. +#4. If TMP >= 45 or the temperature of any one of fan is higher than 50, +# All fans run with duty 100%. +#5. Any one of 6 fans is fault, set duty = 100%. +#6. Direction factor. If it is B2F direction, duty + 12%. + + # MISC: + # 1.Check single LM75 before applied average. + # 2.If no matched fan speed is found from the policy, + # use FAN_DUTY_CYCLE_MIN as default speed + # Get current temperature + # 4.Decision 3: Decide new fan speed depend on fan direction/current fan speed/temperature + + + + +# Make a class we can use to capture stdout and sterr in the log +class accton_as7326_monitor(object): + # static temp var + _ori_temp = 0 + _new_perc = 0 + _ori_perc = 0 + + def __init__(self, log_file, log_level): + """Needs a logger and a logger level.""" + # set up logging to file + logging.basicConfig( + filename=log_file, + filemode='w', + level=log_level, + format= '[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s', + datefmt='%H:%M:%S' + ) + + # set up logging to console + if log_level == logging.DEBUG: + console = logging.StreamHandler() + console.setLevel(log_level) + formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + + logging.debug('SET. logfile:%s / loglevel:%d', log_file, log_level) + + def manage_fans(self): + max_duty = 100 + fan_policy_f2b = { + 0: [32, 0, 105000], + 1: [50, 105000, 120000], + 2: [63, 120000, 135000], + 3: [max_duty, 135000, sys.maxsize], + } + fan_policy_b2f = { + 0: [44, 0, 105000], + 1: [63, 105000, 120000], + 2: [75, 120000, 135000], + 3: [max_duty, 135000, sys.maxsize], + } + fan_policy_single = { + 0: 40000, + 1: 45000, + 2: 50000, + } + + thermal = ThermalUtil() + fan = FanUtil() + for x in range(fan.get_idx_fan_start(), fan.get_num_fans()+1): + fan_status = fan.get_fan_status(x) + if fan_status is None: + logging.debug('INFO. SET new_perc to %d (FAN stauts is None. fan_num:%d)', max_duty, x) + return False + if fan_status is False: + logging.debug('INFO. SET new_perc to %d (FAN fault. fan_num:%d)', max_duty, x) + fan.set_fan_duty_cycle(max_duty) + return True + #logging.debug('INFO. fan_status is True (fan_num:%d)', x) + + fan_dir=fan.get_fan_dir(1) + if fan_dir == 1: + fan_policy = fan_policy_f2b + else: + fan_policy = fan_policy_b2f + + #Decide fan duty by if any of sensors > fan_policy_single. + new_duty_cycle = fan_policy[0][0] + for x in range(thermal.get_idx_thermal_start(), thermal.get_num_thermals()+1): + single_thm = thermal._get_thermal_node_val(x) + for y in range(0, len(fan_policy_single)): + if single_thm > fan_policy_single[y]: + if fan_policy[y+1][0] > new_duty_cycle: + new_duty_cycle = fan_policy[y+1][0] + logging.debug('INFO. Single thermal sensor %d with temp %d > %d , new_duty_cycle=%d', + x, single_thm, fan_policy_single[y], new_duty_cycle) + single_result = new_duty_cycle + + + #Find if current duty matched any of define duty. + #If not, set it to highest one. + cur_duty_cycle = fan.get_fan_duty_cycle() + for x in range(0, len(fan_policy)): + if cur_duty_cycle == fan_policy[x][0]: + break + if x == len(fan_policy) : + fan.set_fan_duty_cycle(fan_policy[0][0]) + cur_duty_cycle = max_duty + + #Decide fan duty by if sum of sensors falls into any of fan_policy{} + get_temp = thermal.get_thermal_temp() + new_duty_cycle = cur_duty_cycle + for x in range(0, len(fan_policy)): + y = len(fan_policy) - x -1 #checked from highest + if get_temp > fan_policy[y][1] and get_temp < fan_policy[y][2] : + new_duty_cycle= fan_policy[y][0] + logging.debug('INFO. Sum of temp %d > %d , new_duty_cycle=%d', get_temp, fan_policy[y][1], new_duty_cycle) + + sum_result = new_duty_cycle + if (sum_result>single_result): + new_duty_cycle = sum_result; + else: + new_duty_cycle = single_result + + logging.debug('INFO. Final duty_cycle=%d', new_duty_cycle) + if(new_duty_cycle != cur_duty_cycle): + fan.set_fan_duty_cycle(new_duty_cycle) + return True + +def main(argv): + log_file = '%s.log' % FUNCTION_NAME + log_level = logging.INFO + if len(sys.argv) != 1: + try: + opts, args = getopt.getopt(argv,'hdl:',['lfile=']) + except getopt.GetoptError: + print 'Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + for opt, arg in opts: + if opt == '-h': + print 'Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + elif opt in ('-d', '--debug'): + log_level = logging.DEBUG + elif opt in ('-l', '--lfile'): + log_file = arg + + monitor = accton_as7326_monitor(log_file, log_level) + + # Loop forever, doing something useful hopefully: + while True: + monitor.manage_fans() + time.sleep(10) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/platform/broadcom/sonic-platform-modules-accton/as7326-56x/utils/accton_as7326_util.py b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/utils/accton_as7326_util.py new file mode 100755 index 000000000000..900bfa805a21 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7326-56x/utils/accton_as7326_util.py @@ -0,0 +1,577 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 Accton Networks, Inc. +# +# 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 3 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, see . +# +# +# Description: +# Due to adoption of optoe drivers, sideband signals of SFPs are moved +# into cpld drivers. Add a new dict, cpld_of_module, for mapping this +# attributes to corresponding cpld nodes. +# + + + +""" +Usage: %(scriptName)s [options] command object + +options: + -h | --help : this help message + -d | --debug : run with debug mode + -f | --force : ignore error during installation or clean +command: + install : install drivers and generate related sysfs nodes + clean : uninstall drivers and remove related sysfs nodes + show : show all systen status + sff : dump SFP eeprom + set : change board setting with fan|led|sfp +""" + +import os +import commands +import sys, getopt +import logging +import re +import time +from collections import namedtuple + + + + +PROJECT_NAME = 'as7326_56x' +version = '0.1.0' +verbose = False +DEBUG = False +args = [] +ALL_DEVICE = {} +DEVICE_NO = {'led':5, 'fan':6,'thermal':4, 'psu':2, 'sfp':58} +FORCE = 0 +#logging.basicConfig(filename= PROJECT_NAME+'.log', filemode='w',level=logging.DEBUG) +#logging.basicConfig(level=logging.INFO) + + +if DEBUG == True: + print sys.argv[0] + print 'ARGV :', sys.argv[1:] + + +def main(): + global DEBUG + global args + global FORCE + + if len(sys.argv)<2: + show_help() + + options, args = getopt.getopt(sys.argv[1:], 'hdf', ['help', + 'debug', + 'force', + ]) + if DEBUG == True: + print options + print args + print len(sys.argv) + + for opt, arg in options: + if opt in ('-h', '--help'): + show_help() + elif opt in ('-d', '--debug'): + DEBUG = True + logging.basicConfig(level=logging.INFO) + elif opt in ('-f', '--force'): + FORCE = 1 + else: + logging.info('no option') + for arg in args: + if arg == 'install': + do_install() + elif arg == 'clean': + do_uninstall() + elif arg == 'show': + device_traversal() + elif arg == 'sff': + if len(args)!=2: + show_eeprom_help() + elif int(args[1]) ==0 or int(args[1]) > DEVICE_NO['sfp']: + show_eeprom_help() + else: + show_eeprom(args[1]) + return + elif arg == 'set': + if len(args)<3: + show_set_help() + else: + set_device(args[1:]) + return + else: + show_help() + + + return 0 + +def show_help(): + print __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]} + sys.exit(0) + +def show_set_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print cmd +" [led|sfp|fan]" + print " use \""+ cmd + " led 0-4 \" to set led color" + print " use \""+ cmd + " fan 0-100\" to set fan duty percetage" + print " use \""+ cmd + " sfp 1-56 {0|1}\" to set sfp# tx_disable" + sys.exit(0) + +def show_eeprom_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print " use \""+ cmd + " 1-56 \" to dump sfp# eeprom" + sys.exit(0) + +def my_log(txt): + if DEBUG == True: + print "[ROY]"+txt + return + +def log_os_system(cmd, show): + logging.info('Run :'+cmd) + status, output = commands.getstatusoutput(cmd) + my_log (cmd +"with result:" + str(status)) + my_log (" output:"+output) + if status: + logging.info('Failed :'+cmd) + if show: + print('Failed :'+cmd) + return status, output + +def driver_check(): + ret, lsmod = log_os_system("lsmod| grep accton", 0) + logging.info('mods:'+lsmod) + if len(lsmod) ==0: + return False + return True + + + +kos = [ +'modprobe i2c_dev', +'modprobe i2c_mux_pca954x force_deselect_on_exit=1', +'modprobe accton_i2c_cpld' , +'modprobe ym2651y' , +'modprobe accton_as7326_56x_fan' , +'modprobe optoe' , +'modprobe accton_as7326_56x_leds' , +'modprobe accton_as7326_56x_psu' ] + +def driver_install(): + global FORCE + status, output = log_os_system("depmod", 1) + for i in range(0,len(kos)): + status, output = log_os_system(kos[i], 1) + if status: + if FORCE == 0: + return status + return 0 + +def driver_uninstall(): + global FORCE + for i in range(0,len(kos)): + rm = kos[-(i+1)].replace("modprobe", "modprobe -rq") + rm = rm.replace("insmod", "rmmod") + lst = rm.split(" ") + if len(lst) > 3: + del(lst[3]) + rm = " ".join(lst) + status, output = log_os_system(rm, 1) + if status: + if FORCE == 0: + return status + return 0 + +led_prefix ='/sys/class/leds/accton_'+PROJECT_NAME+'_led::' +hwmon_types = {'led': ['diag','fan','loc','psu1','psu2']} +hwmon_nodes = {'led': ['brightness'] } +hwmon_prefix ={'led': led_prefix} + +i2c_prefix = '/sys/bus/i2c/devices/' +i2c_bus = {'fan': ['11-0066'] , + 'thermal': ['15-0048','15-0049', '15-004a', '15-004b'] , + 'psu': ['17-0051','13-0053'], + 'sfp': ['-0050']} +i2c_nodes = {'fan': ['present', 'front_speed_rpm', 'rear_speed_rpm'] , + 'thermal': ['hwmon/hwmon*/temp1_input'] , + 'psu': ['psu_present ', 'psu_power_good'] , + 'sfp': ['module_present_', 'module_tx_disable_']} + +sfp_map = [ + 42,41,44,43,47,45,46,50, + 48,49,51,52,53,56,55,54, + 58,57,59,60,61,63,62,64, + 66,68,65,67,69,71,72,70, + 74,73,76,75,77,79,78,80, + 81,82,84,85,83,87,88,86, #port 41~48 + 25,26,27,28,29,30,31,32, #port 49~56 QSFP + 22,23] #port 57~58 SFP+ from CPU NIF. +qsfp_start = 48 +qsfp_end = 56 + +#For sideband signals of SFP/QSFP modules. +cpld_of_module = {'12-0062': list(range(0,30)), + '18-0060': list(range(30,58)) } + + +mknod =[ +'echo pca9548 0x77 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-1/new_device' , +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-1/new_device' , +'echo pca9548 0x72 > /sys/bus/i2c/devices/i2c-24/new_device' , +'echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-2/new_device' , +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-33/new_device', +'echo pca9548 0x72 > /sys/bus/i2c/devices/i2c-34/new_device', +'echo pca9548 0x73 > /sys/bus/i2c/devices/i2c-35/new_device', +'echo pca9548 0x74 > /sys/bus/i2c/devices/i2c-36/new_device', +'echo pca9548 0x75 > /sys/bus/i2c/devices/i2c-37/new_device', +'echo pca9548 0x76 > /sys/bus/i2c/devices/i2c-38/new_device', +'echo 24c04 0x56 > /sys/bus/i2c/devices/i2c-0/new_device', + +'echo as7326_56x_fan 0x66 > /sys/bus/i2c/devices/i2c-11/new_device ', +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-15/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-15/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-15/new_device', +'echo lm75 0x4b > /sys/bus/i2c/devices/i2c-15/new_device', +'echo as7326_56x_psu1 0x51 > /sys/bus/i2c/devices/i2c-17/new_device', +'echo ym2651 0x59 > /sys/bus/i2c/devices/i2c-17/new_device', +'echo as7326_56x_psu2 0x53 > /sys/bus/i2c/devices/i2c-13/new_device', +'echo ym2651 0x5b > /sys/bus/i2c/devices/i2c-13/new_device', +'echo as7326_56x_cpld1 0x60 > /sys/bus/i2c/devices/i2c-18/new_device', +'echo as7326_56x_cpld2 0x62 > /sys/bus/i2c/devices/i2c-12/new_device', +'echo as7326_56x_cpld3 0x64 > /sys/bus/i2c/devices/i2c-19/new_device'] + +mknod2 =[ +] + + + +def i2c_order_check(): + # This project has only 1 i2c bus. + return 0 + +def device_install(): + global FORCE + + order = i2c_order_check() + + # if 0x70 is not exist @i2c-1, use reversed bus order + if order: + for i in range(0,len(mknod2)): + #for pca954x need times to built new i2c buses + if mknod2[i].find('pca954') != -1: + time.sleep(1) + + status, output = log_os_system(mknod2[i], 1) + if status: + print output + if FORCE == 0: + return status + else: + for i in range(0,len(mknod)): + #for pca954x need times to built new i2c buses + if mknod[i].find('pca954') != -1: + time.sleep(1) + + status, output = log_os_system(mknod[i], 1) + if status: + print output + if FORCE == 0: + return status + for i in range(0,len(sfp_map)): + if i < qsfp_start or i >= qsfp_end: + status, output =log_os_system("echo optoe2 0x50 > /sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/new_device", 1) + else: + status, output =log_os_system("echo optoe1 0x50 > /sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/new_device", 1) + if status: + print output + if FORCE == 0: + return status + return + +def device_uninstall(): + global FORCE + + status, output =log_os_system("ls /sys/bus/i2c/devices/1-0076", 0) + if status==0: + I2C_ORDER=1 + else: + I2C_ORDER=0 + + for i in range(0,len(sfp_map)): + target = "/sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/delete_device" + status, output =log_os_system("echo 0x50 > "+ target, 1) + if status: + print output + if FORCE == 0: + return status + + if I2C_ORDER==0: + nodelist = mknod + else: + nodelist = mknod2 + + for i in range(len(nodelist)): + target = nodelist[-(i+1)] + temp = target.split() + del temp[1] + temp[-1] = temp[-1].replace('new_device', 'delete_device') + status, output = log_os_system(" ".join(temp), 1) + if status: + print output + if FORCE == 0: + return status + + return + +def system_ready(): + if driver_check() == False: + return False + if not device_exist(): + return False + return True + +def do_install(): + print "Checking system...." + if driver_check() == False: + print "No driver, installing...." + status = driver_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" drivers detected...." + if not device_exist(): + print "No device, installing...." + status = device_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" devices detected...." + return + +def do_uninstall(): + print "Checking system...." + if not device_exist(): + print PROJECT_NAME.upper() +" has no device installed...." + else: + print "Removing device...." + status = device_uninstall() + if status: + if FORCE == 0: + return status + + if driver_check()== False : + print PROJECT_NAME.upper() +" has no driver installed...." + else: + print "Removing installed driver...." + status = driver_uninstall() + if status: + if FORCE == 0: + return status + + return + +def devices_info(): + global DEVICE_NO + global ALL_DEVICE + global i2c_bus, hwmon_types + for key in DEVICE_NO: + ALL_DEVICE[key]= {} + for i in range(0,DEVICE_NO[key]): + ALL_DEVICE[key][key+str(i+1)] = [] + + for key in i2c_bus: + buses = i2c_bus[key] + nodes = i2c_nodes[key] + for i in range(0,len(buses)): + for j in range(0,len(nodes)): + if 'fan' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ buses[i]+"/fan"+str(k+1)+"_"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + elif 'sfp' == key: + for k in range(0,DEVICE_NO[key]): + for lk in cpld_of_module: + if k in cpld_of_module[lk]: + cpld_str = lk + node = key+str(k+1) + path = i2c_prefix+ lk + "/"+ nodes[j] + str(k+1) + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + else: + node = key+str(i+1) + path = i2c_prefix+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + + for key in hwmon_types: + itypes = hwmon_types[key] + nodes = hwmon_nodes[key] + for i in range(0,len(itypes)): + for j in range(0,len(nodes)): + node = key+"_"+itypes[i] + path = hwmon_prefix[key]+ itypes[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][ key+str(i+1)].append(path) + + #show dict all in the order + if DEBUG == True: + for i in sorted(ALL_DEVICE.keys()): + print(i+": ") + for j in sorted(ALL_DEVICE[i].keys()): + print(" "+j) + for k in (ALL_DEVICE[i][j]): + print(" "+" "+k) + return + +def show_eeprom(index): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + node = ALL_DEVICE['sfp'] ['sfp'+str(index)][0] + node = node.replace(node.split("/")[-1], 'eeprom') + # check if got hexdump command in current environment + ret, log = log_os_system("which hexdump", 0) + ret, log2 = log_os_system("which busybox hexdump", 0) + if len(log): + hex_cmd = 'hexdump' + elif len(log2): + hex_cmd = ' busybox hexdump' + else: + log = 'Failed : no hexdump cmd!!' + logging.info(log) + print log + return 1 + + print node + ":" + ret, log = log_os_system("cat "+node+"| "+hex_cmd+" -C", 1) + if ret==0: + print log + else: + print "**********device no found**********" + return + +def set_device(args): + global DEVICE_NO + global ALL_DEVICE + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + + if args[0]=='led': + if int(args[1])>4: + show_set_help() + return + #print ALL_DEVICE['led'] + for i in range(0,len(ALL_DEVICE['led'])): + for k in (ALL_DEVICE['led']['led'+str(i+1)]): + ret, log = log_os_system("echo "+args[1]+" >"+k, 1) + if ret: + return ret + elif args[0]=='fan': + if int(args[1])>100: + show_set_help() + return + #print ALL_DEVICE['fan'] + #fan1~6 is all fine, all fan share same setting + node = ALL_DEVICE['fan'] ['fan1'][0] + node = node.replace(node.split("/")[-1], 'fan_duty_cycle_percentage') + ret, log = log_os_system("cat "+ node, 1) + if ret==0: + print ("Previous fan duty: " + log.strip() +"%") + ret, log = log_os_system("echo "+args[1]+" >"+node, 1) + if ret==0: + print ("Current fan duty: " + args[1] +"%") + return ret + elif args[0]=='sfp': + if int(args[1])> DEVICE_NO[args[0]] or int(args[1])==0: + show_set_help() + return + if len(args)<2: + show_set_help() + return + + if int(args[2])>1: + show_set_help() + return + + #print ALL_DEVICE[args[0]] + for i in range(0,len(ALL_DEVICE[args[0]])): + for j in ALL_DEVICE[args[0]][args[0]+str(args[1])]: + if j.find('tx_disable')!= -1: + ret, log = log_os_system("echo "+args[2]+" >"+ j, 1) + if ret: + return ret + return + +#get digits inside a string. +#Ex: 31 for "sfp31" +def get_value(input): + digit = re.findall('\d+', input) + return int(digit[0]) + +def device_traversal(): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + for i in sorted(ALL_DEVICE.keys()): + print("============================================") + print(i.upper()+": ") + print("============================================") + + for j in sorted(ALL_DEVICE[i].keys(), key=get_value): + print " "+j+":", + for k in (ALL_DEVICE[i][j]): + ret, log = log_os_system("cat "+k, 0) + func = k.split("/")[-1].strip() + func = re.sub(j+'_','',func,1) + func = re.sub(i.lower()+'_','',func,1) + if ret==0: + print func+"="+log+" ", + else: + print func+"="+"X"+" ", + print + print("----------------------------------------------------------------") + + print + return + +def device_exist(): + ret1, log = log_os_system("ls "+i2c_prefix+"*0070", 0) + ret2, log = log_os_system("ls "+i2c_prefix+"i2c-2", 0) + return not(ret1 or ret2) + +if __name__ == "__main__": + main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/classes/__init__.py b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/classes/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/Makefile b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/Makefile new file mode 100644 index 000000000000..d9566355b849 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/Makefile @@ -0,0 +1,2 @@ +obj-m:=accton_as7712_32x_fan.o accton_as7712_32x_sfp.o leds-accton_as7712_32x.o \ + accton_as7712_32x_psu.o accton_i2c_cpld.o ym2651y.o diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_as7712_32x_fan.c b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_as7712_32x_fan.c new file mode 100644 index 000000000000..3eae51cbcea7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_as7712_32x_fan.c @@ -0,0 +1,776 @@ +/* + * A hwmon driver for the Accton as7712 32x fan + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as7712_32x_fan" + +#define NUM_THERMAL_SENSORS (3) /* Get sum of this number of sensors.*/ +#define THERMAL_SENSORS_DRIVER "lm75" +#define THERMAL_SENSORS_ADDRS {0x48, 0x49, 0x4a} + +#define IN +#define OUT + +static struct as7712_32x_fan_data *as7712_32x_fan_update_device(struct device *dev); +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t get_enable(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t get_sys_temp(struct device *dev, struct device_attribute *da, char *buf); +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +/* fan related data, the index should match sysfs_fan_attributes + */ +static const u8 fan_reg[] = { + 0x0F, /* fan 1-6 present status */ + 0x11, /* fan PWM(for all fan) */ + 0x12, /* front fan 1 speed(rpm) */ + 0x13, /* front fan 2 speed(rpm) */ + 0x14, /* front fan 3 speed(rpm) */ + 0x15, /* front fan 4 speed(rpm) */ + 0x16, /* front fan 5 speed(rpm) */ + 0x17, /* front fan 6 speed(rpm) */ + 0x22, /* rear fan 1 speed(rpm) */ + 0x23, /* rear fan 2 speed(rpm) */ + 0x24, /* rear fan 3 speed(rpm) */ + 0x25, /* rear fan 4 speed(rpm) */ + 0x26, /* rear fan 5 speed(rpm) */ + 0x27, /* rear fan 6 speed(rpm) */ +}; + +/* Each client has this additional data */ +struct as7712_32x_fan_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[ARRAY_SIZE(fan_reg)]; /* Register value */ + u8 enable; + int system_temp; /*In unit of mini-Celsius*/ + int sensors_found; +}; + +enum fan_id { + FAN1_ID, + FAN2_ID, + FAN3_ID, + FAN4_ID, + FAN5_ID, + FAN6_ID +}; + +enum sysfs_fan_attributes { + FAN_PRESENT_REG, + FAN_DUTY_CYCLE_PERCENTAGE, /* Only one CPLD register to control duty cycle for all fans */ + FAN1_FRONT_SPEED_RPM, + FAN2_FRONT_SPEED_RPM, + FAN3_FRONT_SPEED_RPM, + FAN4_FRONT_SPEED_RPM, + FAN5_FRONT_SPEED_RPM, + FAN6_FRONT_SPEED_RPM, + FAN1_REAR_SPEED_RPM, + FAN2_REAR_SPEED_RPM, + FAN3_REAR_SPEED_RPM, + FAN4_REAR_SPEED_RPM, + FAN5_REAR_SPEED_RPM, + FAN6_REAR_SPEED_RPM, + FAN1_PRESENT, + FAN2_PRESENT, + FAN3_PRESENT, + FAN4_PRESENT, + FAN5_PRESENT, + FAN6_PRESENT, + FAN1_FAULT, + FAN2_FAULT, + FAN3_FAULT, + FAN4_FAULT, + FAN5_FAULT, + FAN6_FAULT +}; + +/* Define attributes + */ +#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT);\ + static SENSOR_DEVICE_ATTR(fan##index2##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT) +#define DECLARE_FAN_FAULT_ATTR(index, index2) &sensor_dev_attr_fan##index##_fault.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_fault.dev_attr.attr + +#define DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_direction, S_IRUGO, fan_show_value, NULL, FAN##index##_DIRECTION) +#define DECLARE_FAN_DIRECTION_ATTR(index) &sensor_dev_attr_fan##index##_direction.dev_attr.attr + +#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN_DUTY_CYCLE_PERCENTAGE);\ + static SENSOR_DEVICE_ATTR(pwm##index, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN_DUTY_CYCLE_PERCENTAGE);\ + static SENSOR_DEVICE_ATTR(pwm##index##_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, FAN_DUTY_CYCLE_PERCENTAGE) + +#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) &sensor_dev_attr_fan_duty_cycle_percentage.dev_attr.attr, \ + &sensor_dev_attr_pwm##index.dev_attr.attr, \ + &sensor_dev_attr_pwm##index##_enable.dev_attr.attr + +#define DECLARE_FAN_SYSTEM_TEMP_SENSOR_DEV_ATTR() \ + static SENSOR_DEVICE_ATTR(sys_temp, S_IRUGO, get_sys_temp, NULL, FAN_DUTY_CYCLE_PERCENTAGE) + +#define DECLARE_FAN_SYSTEM_TEMP_ATTR() &sensor_dev_attr_sys_temp.dev_attr.attr + + +#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, fan_show_value, NULL, FAN##index##_PRESENT) +#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr + +#define DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_front_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_rear_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_input, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index2##_input, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM) +#define DECLARE_FAN_SPEED_RPM_ATTR(index, index2) &sensor_dev_attr_fan##index##_front_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_rear_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_input.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_input.dev_attr.attr + +/* 6 fan fault attributes in this platform */ +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1,11); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2,12); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(3,13); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(4,14); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(5,15); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(6,16); +/* 6 fan speed(rpm) attributes in this platform */ +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1,11); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2,12); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(3,13); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(4,14); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(5,15); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(6,16); +/* 6 fan present attributes in this platform */ +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(5); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(6); +/* 1 fan duty cycle attribute in this platform */ +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(1); +/* System temperature for fancontrol */ +DECLARE_FAN_SYSTEM_TEMP_SENSOR_DEV_ATTR(); + +static struct attribute *as7712_32x_fan_attributes[] = { + /* fan related attributes */ + DECLARE_FAN_FAULT_ATTR(1,11), + DECLARE_FAN_FAULT_ATTR(2,12), + DECLARE_FAN_FAULT_ATTR(3,13), + DECLARE_FAN_FAULT_ATTR(4,14), + DECLARE_FAN_FAULT_ATTR(5,15), + DECLARE_FAN_FAULT_ATTR(6,16), + DECLARE_FAN_SPEED_RPM_ATTR(1,11), + DECLARE_FAN_SPEED_RPM_ATTR(2,12), + DECLARE_FAN_SPEED_RPM_ATTR(3,13), + DECLARE_FAN_SPEED_RPM_ATTR(4,14), + DECLARE_FAN_SPEED_RPM_ATTR(5,15), + DECLARE_FAN_SPEED_RPM_ATTR(6,16), + DECLARE_FAN_PRESENT_ATTR(1), + DECLARE_FAN_PRESENT_ATTR(2), + DECLARE_FAN_PRESENT_ATTR(3), + DECLARE_FAN_PRESENT_ATTR(4), + DECLARE_FAN_PRESENT_ATTR(5), + DECLARE_FAN_PRESENT_ATTR(6), + DECLARE_FAN_DUTY_CYCLE_ATTR(1), + DECLARE_FAN_SYSTEM_TEMP_ATTR(), + NULL +}; + +#define FAN_DUTY_CYCLE_REG_MASK 0xF +#define FAN_MAX_DUTY_CYCLE 100 +#define FAN_REG_VAL_TO_SPEED_RPM_STEP 100 + +static int as7712_32x_fan_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int as7712_32x_fan_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* fan utility functions + */ +static u32 reg_val_to_duty_cycle(u8 reg_val) +{ + reg_val &= FAN_DUTY_CYCLE_REG_MASK; + return ((u32)(reg_val+1) * 625 + 75)/ 100; +} + +static u8 duty_cycle_to_reg_val(u8 duty_cycle) +{ + return ((u32)duty_cycle * 100 / 625) - 1; +} + +static u32 reg_val_to_speed_rpm(u8 reg_val) +{ + return (u32)reg_val * FAN_REG_VAL_TO_SPEED_RPM_STEP; +} + +static u8 reg_val_to_is_present(u8 reg_val, enum fan_id id) +{ + u8 mask = (1 << id); + + reg_val &= mask; + + return reg_val ? 0 : 1; +} + +static u8 is_fan_fault(struct as7712_32x_fan_data *data, enum fan_id id) +{ + u8 ret = 1; + int front_fan_index = FAN1_FRONT_SPEED_RPM + id; + int rear_fan_index = FAN1_REAR_SPEED_RPM + id; + + /* Check if the speed of front or rear fan is ZERO, + */ + if (reg_val_to_speed_rpm(data->reg_val[front_fan_index]) && + reg_val_to_speed_rpm(data->reg_val[rear_fan_index])) { + ret = 0; + } + + return ret; +} + +static ssize_t set_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct as7712_32x_fan_data *data = as7712_32x_fan_update_device(dev); + int error, value; + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < 0 || value > 1) + return -EINVAL; + + data->enable = value; + if (value == 0) + { + return set_duty_cycle(dev, da, buf, FAN_MAX_DUTY_CYCLE); + } + return count; +} + + +static ssize_t get_enable(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as7712_32x_fan_data *data = as7712_32x_fan_update_device(dev); + + return sprintf(buf, "%u\n", data->enable); +} +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int error, value; + struct i2c_client *client = to_i2c_client(dev); + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < 0) + return -EINVAL; + + value = (value > FAN_MAX_DUTY_CYCLE)? FAN_MAX_DUTY_CYCLE : value; + + as7712_32x_fan_write_value(client, 0x33, 0); /* Disable fan speed watch dog */ + as7712_32x_fan_write_value(client, fan_reg[FAN_DUTY_CYCLE_PERCENTAGE], duty_cycle_to_reg_val(value)); + return count; +} + +/* Due to this struct is declared at lm75.c, it cannot be include + * under Sonic environment. I duplicate it from lm75.c. + */ +struct lm75_data { + struct i2c_client *client; + struct device *hwmon_dev; + struct thermal_zone_device *tz; + struct mutex update_lock; + u8 orig_conf; + u8 resolution; /* In bits, between 9 and 12 */ + u8 resolution_limits; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long sample_time; /* In jiffies */ + s16 temp[3]; /* Register values, + 0 = input + 1 = max + 2 = hyst */ +}; + +/*Copied from lm75.c*/ +static inline long lm75_reg_to_mc(s16 temp, u8 resolution) +{ + return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); +} + +/*Get hwmon_dev from i2c_client, set hwmon_dev = NULL is failed.*/ +static struct device * get_hwmon_dev( + struct i2c_client *client) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + if(data) + { + if( data->valid == 1 && data->hwmon_dev) + { + return data->hwmon_dev; + } + + } + return NULL; +} + +/* To find hwmon index by opening hwmon under that i2c address. + */ +static int find_hwmon_index_by_FileOpen( + int bus_nr, + unsigned short addr, + OUT int *index) +{ +#define MAX_HWMON_DEVICE (10) /* Find hwmon device in 0~10*/ + struct file *sfd; + char client_name[96]; + int i=0; + + do { + snprintf(client_name, sizeof(client_name), + "/sys/bus/i2c/devices/%d-%04x/hwmon/hwmon%d/temp1_input", + bus_nr, addr, i); + + sfd = filp_open(client_name, O_RDONLY, 0); + i++; + } while( IS_ERR(sfd) && i < MAX_HWMON_DEVICE); + + if (IS_ERR(sfd)) { + pr_err("Failed to open file(%s)#%d\r\n", client_name, __LINE__); + return -ENOENT; + } + filp_close(sfd, 0); + *index = i - 1; + return 0; + +#undef MAX_HWMON_DEVICE +} + +static int get_temp_file_path( + int bus_nr, unsigned short addr, + struct device *hwmon_dev + ,char *path, int max_len) +{ + + if(hwmon_dev && strlen(dev_name(hwmon_dev))) + { + snprintf(path, max_len, + "/sys/bus/i2c/devices/%d-%04x/hwmon/%s/temp1_input", + bus_nr, addr, dev_name(hwmon_dev)); + } + else + { + int i=0; + if(find_hwmon_index_by_FileOpen( bus_nr, addr, &i)) + { + return -EIO; + } + snprintf(path, max_len, + "/sys/bus/i2c/devices/%d-%04x/hwmon/hwmon%d/temp1_input", + bus_nr, addr, i); + } + return 0; +} + +/*File read the dev file at user space.*/ +static int read_devfile_temp1_input( + struct device *dev, + int bus_nr, + unsigned short addr, + struct device *hwmon_dev, + int *miniCelsius) +{ + struct file *sfd; + char buffer[96]; + char devfile[96]; + int rc, status; + int rdlen, value; + mm_segment_t old_fs; + + rc = 0; + get_temp_file_path(bus_nr, addr, hwmon_dev, devfile, sizeof(devfile)); + sfd = filp_open(devfile, O_RDONLY, 0); + if (IS_ERR(sfd)) { + pr_err("Failed to open file(%s)#%d\r\n", devfile, __LINE__); + return -ENOENT; + } + dev_dbg(dev, "Found device:%s\n",devfile); + + if(!(sfd->f_op) || !(sfd->f_op->read) ) { + pr_err("file %s cann't readable ?\n",devfile); + return -ENOENT; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + rdlen = sfd->f_op->read(sfd, buffer, sizeof(buffer), &sfd->f_pos); + if (rdlen == 0) { + pr_err( "File(%s) empty!\n", devfile); + rc = -EIO; + goto exit; + } + status = sscanf(buffer, "%d", &value); + if (status != 1) { + rc = -EIO; + goto exit; + } + *miniCelsius = value; + dev_dbg(dev,"found sensors: %d @i2c %d-%04x\n", value, bus_nr, addr); + +exit: + set_fs(old_fs); + filp_close(sfd, 0); + return rc; +} + +static u8 is_lm75_data_due(struct i2c_client *client) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + if (time_after(jiffies, data->last_updated + data->sample_time)) + { + return 1; + } + return 0; +} +static int get_lm75_temp(struct i2c_client *client, int *miniCelsius) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + *miniCelsius = lm75_reg_to_mc(data->temp[0], data->resolution); + + return 0; +} + +static bool lm75_addr_mached(unsigned short addr) +{ + int i; + unsigned short addrs[] = THERMAL_SENSORS_ADDRS; + + for (i = 0; i < ARRAY_SIZE(addrs); i++) + { + if( addr == addrs[i]) + return 1; + } + return 0; +} + +static int _find_lm75_device(struct device *dev, void *data) +{ + struct device_driver *driver; + struct as7712_32x_fan_data *prv = data; + char *driver_name = THERMAL_SENSORS_DRIVER; + + driver = dev->driver; + if (driver && driver->name && + strcmp(driver->name, driver_name) == 0) + { + struct i2c_client *client; + client = to_i2c_client(dev); + if (client) + { + /*cannot use "struct i2c_adapter *adap = to_i2c_adapter(dev);"*/ + struct i2c_adapter *adap = client->adapter; + int miniCelsius = 0; + + if (! lm75_addr_mached(client->addr)) + { + return 0; + } + + if (!adap) { + return -ENXIO; + } + + /* If the data is not updated, read them from devfile + to drive them updateing data from chip.*/ + if (is_lm75_data_due(client)) + { + struct device *hwmon_dev; + + hwmon_dev = get_hwmon_dev(client); + if(0 == read_devfile_temp1_input(dev, adap->nr, + client->addr, hwmon_dev, &miniCelsius)) + { + prv->system_temp += miniCelsius; + prv->sensors_found++; + } + + } + else + { + get_lm75_temp(client, &miniCelsius); + prv->system_temp += miniCelsius; + prv->sensors_found++; + + } + } + } + return 0; +} + +/*Find all lm75 devices and return sum of temperatures.*/ +static ssize_t get_sys_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + ssize_t ret = 0; + struct as7712_32x_fan_data *data = as7712_32x_fan_update_device(dev); + + data->system_temp=0; + data->sensors_found=0; + i2c_for_each_dev(data, _find_lm75_device); + if (NUM_THERMAL_SENSORS != data->sensors_found) + { + dev_dbg(dev,"only %d of %d temps are found\n", + data->sensors_found, NUM_THERMAL_SENSORS); + data->system_temp = INT_MAX; + } + ret = sprintf(buf, "%d\n",data->system_temp); + return ret; +} + +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7712_32x_fan_data *data = as7712_32x_fan_update_device(dev); + ssize_t ret = 0; + + if (data->valid) { + switch (attr->index) { + case FAN_DUTY_CYCLE_PERCENTAGE: + { + u32 duty_cycle = reg_val_to_duty_cycle(data->reg_val[FAN_DUTY_CYCLE_PERCENTAGE]); + ret = sprintf(buf, "%u\n", duty_cycle); + break; + } + case FAN1_FRONT_SPEED_RPM: + case FAN2_FRONT_SPEED_RPM: + case FAN3_FRONT_SPEED_RPM: + case FAN4_FRONT_SPEED_RPM: + case FAN5_FRONT_SPEED_RPM: + case FAN6_FRONT_SPEED_RPM: + case FAN1_REAR_SPEED_RPM: + case FAN2_REAR_SPEED_RPM: + case FAN3_REAR_SPEED_RPM: + case FAN4_REAR_SPEED_RPM: + case FAN5_REAR_SPEED_RPM: + case FAN6_REAR_SPEED_RPM: + ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index])); + break; + case FAN1_PRESENT: + case FAN2_PRESENT: + case FAN3_PRESENT: + case FAN4_PRESENT: + case FAN5_PRESENT: + case FAN6_PRESENT: + ret = sprintf(buf, "%d\n", + reg_val_to_is_present(data->reg_val[FAN_PRESENT_REG], + attr->index - FAN1_PRESENT)); + break; + case FAN1_FAULT: + case FAN2_FAULT: + case FAN3_FAULT: + case FAN4_FAULT: + case FAN5_FAULT: + case FAN6_FAULT: + ret = sprintf(buf, "%d\n", is_fan_fault(data, attr->index - FAN1_FAULT)); + break; + default: + break; + } + } + + return ret; +} + +static const struct attribute_group as7712_32x_fan_group = { + .attrs = as7712_32x_fan_attributes, +}; + +static struct as7712_32x_fan_data *as7712_32x_fan_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7712_32x_fan_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || + !data->valid) { + int i; + + dev_dbg(&client->dev, "Starting as7712_32x_fan update\n"); + data->valid = 0; + + /* Update fan data + */ + for (i = 0; i < ARRAY_SIZE(data->reg_val); i++) { + int status = as7712_32x_fan_read_value(client, fan_reg[i]); + + if (status < 0) { + data->valid = 0; + mutex_unlock(&data->update_lock); + dev_dbg(&client->dev, "reg %d, err %d\n", fan_reg[i], status); + return data; + } + else { + data->reg_val[i] = status; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int as7712_32x_fan_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7712_32x_fan_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7712_32x_fan_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->enable = 0; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7712_32x_fan_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: fan '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7712_32x_fan_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7712_32x_fan_remove(struct i2c_client *client) +{ + struct as7712_32x_fan_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7712_32x_fan_group); + + return 0; +} + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x66, I2C_CLIENT_END }; + +static const struct i2c_device_id as7712_32x_fan_id[] = { + { "as7712_32x_fan", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7712_32x_fan_id); + +static struct i2c_driver as7712_32x_fan_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .probe = as7712_32x_fan_probe, + .remove = as7712_32x_fan_remove, + .id_table = as7712_32x_fan_id, + .address_list = normal_i2c, +}; + +static int __init as7712_32x_fan_init(void) +{ + return i2c_add_driver(&as7712_32x_fan_driver); +} + +static void __exit as7712_32x_fan_exit(void) +{ + i2c_del_driver(&as7712_32x_fan_driver); +} + +module_init(as7712_32x_fan_init); +module_exit(as7712_32x_fan_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7712_32x_fan driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_as7712_32x_psu.c b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_as7712_32x_psu.c new file mode 100644 index 000000000000..233aef78e09f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_as7712_32x_psu.c @@ -0,0 +1,288 @@ +/* + * An hwmon driver for accton as7712_32x Power Module + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, char *buf); +static int as7712_32x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x50, 0x53, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as7712_32x_psu_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + u8 status; /* Status(present/power_good) register read from CPLD */ + char model_name[9]; /* Model name, read from eeprom */ +}; + +static struct as7712_32x_psu_data *as7712_32x_psu_update_device(struct device *dev); + +enum as7712_32x_psu_sysfs_attributes { + PSU_PRESENT, + PSU_MODEL_NAME, + PSU_POWER_GOOD +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_status, NULL, PSU_PRESENT); +static SENSOR_DEVICE_ATTR(psu_model_name, S_IRUGO, show_model_name,NULL, PSU_MODEL_NAME); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD); + +static struct attribute *as7712_32x_psu_attributes[] = { + &sensor_dev_attr_psu_present.dev_attr.attr, + &sensor_dev_attr_psu_model_name.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + NULL +}; + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7712_32x_psu_data *data = as7712_32x_psu_update_device(dev); + u8 status = 0; + + if (attr->index == PSU_PRESENT) { + status = !(data->status >> (1-data->index) & 0x1); + } + else { /* PSU_POWER_GOOD */ + status = (data->status >> (3-data->index) & 0x1); + } + + return sprintf(buf, "%d\n", status); +} + +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as7712_32x_psu_data *data = as7712_32x_psu_update_device(dev); + + return sprintf(buf, "%s\n", data->model_name); +} + +static const struct attribute_group as7712_32x_psu_group = { + .attrs = as7712_32x_psu_attributes, +}; + +static int as7712_32x_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7712_32x_psu_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7712_32x_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7712_32x_psu_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7712_32x_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7712_32x_psu_remove(struct i2c_client *client) +{ + struct as7712_32x_psu_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7712_32x_psu_group); + kfree(data); + + return 0; +} + +enum psu_index +{ + as7712_32x_psu1, + as7712_32x_psu2 +}; + +static const struct i2c_device_id as7712_32x_psu_id[] = { + { "as7712_32x_psu1", as7712_32x_psu1 }, + { "as7712_32x_psu2", as7712_32x_psu2 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7712_32x_psu_id); + +static struct i2c_driver as7712_32x_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7712_32x_psu", + }, + .probe = as7712_32x_psu_probe, + .remove = as7712_32x_psu_remove, + .id_table = as7712_32x_psu_id, + .address_list = normal_i2c, +}; + +static int as7712_32x_psu_read_block(struct i2c_client *client, u8 command, u8 *data, + int data_len) +{ + int result = 0; + int retry_count = 5; + + while (retry_count) { + retry_count--; + + result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); + + if (unlikely(result < 0)) { + msleep(10); + continue; + } + + if (unlikely(result != data_len)) { + result = -EIO; + msleep(10); + continue; + } + + result = 0; + break; + } + + return result; +} + +static struct as7712_32x_psu_data *as7712_32x_psu_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7712_32x_psu_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int status; + int power_good = 0; + + dev_dbg(&client->dev, "Starting as7712_32x update\n"); + + /* Read psu status */ + status = accton_i2c_cpld_read(0x60, 0x2); + + if (status < 0) { + dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status); + } + else { + data->status = status; + } + + /* Read model name */ + memset(data->model_name, 0, sizeof(data->model_name)); + power_good = (data->status >> (3-data->index) & 0x1); + + if (power_good) { + status = as7712_32x_psu_read_block(client, 0x20, data->model_name, + ARRAY_SIZE(data->model_name)-1); + + if (status < 0) { + data->model_name[0] = '\0'; + dev_dbg(&client->dev, "unable to read model name from (0x%x)\n", client->addr); + } + else { + data->model_name[ARRAY_SIZE(data->model_name)-1] = '\0'; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init as7712_32x_psu_init(void) +{ + return i2c_add_driver(&as7712_32x_psu_driver); +} + +static void __exit as7712_32x_psu_exit(void) +{ + i2c_del_driver(&as7712_32x_psu_driver); +} + +module_init(as7712_32x_psu_init); +module_exit(as7712_32x_psu_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7712_32x_psu driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_as7712_32x_sfp.c b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_as7712_32x_sfp.c new file mode 100644 index 000000000000..4668f5a7f050 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_as7712_32x_sfp.c @@ -0,0 +1,1171 @@ +/* + * SFP driver for accton as7712_32x sfp + * + * Copyright (C) Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "as7712_32x_sfp" + +#define DEBUG_MODE 0 + +#if (DEBUG_MODE == 1) + #define DEBUG_PRINT(fmt, args...) \ + printk (KERN_INFO "%s:%s[%d]: " fmt "\r\n", __FILE__, __FUNCTION__, __LINE__, ##args) +#else + #define DEBUG_PRINT(fmt, args...) +#endif + +#define NUM_OF_SFP_PORT 32 +#define EEPROM_NAME "sfp_eeprom" +#define EEPROM_SIZE 256 /* 256 byte eeprom */ +#define BIT_INDEX(i) (1ULL << (i)) +#define USE_I2C_BLOCK_READ 1 +#define I2C_RW_RETRY_COUNT 3 +#define I2C_RW_RETRY_INTERVAL 100 /* ms */ + +#define SFP_EEPROM_A0_I2C_ADDR (0xA0 >> 1) +#define SFP_EEPROM_A2_I2C_ADDR (0xA2 >> 1) + +#define SFF8024_PHYSICAL_DEVICE_ID_ADDR 0x0 +#define SFF8024_DEVICE_ID_SFP 0x3 +#define SFF8024_DEVICE_ID_QSFP 0xC +#define SFF8024_DEVICE_ID_QSFP_PLUS 0xD +#define SFF8024_DEVICE_ID_QSFP28 0x11 + +#define SFF8472_DIAG_MON_TYPE_ADDR 92 +#define SFF8472_DIAG_MON_TYPE_DDM_MASK 0x40 +#define SFF8472_10G_ETH_COMPLIANCE_ADDR 0x3 +#define SFF8472_10G_BASE_MASK 0xF0 + +#define SFF8436_RX_LOS_ADDR 3 +#define SFF8436_TX_FAULT_ADDR 4 +#define SFF8436_TX_DISABLE_ADDR 86 + +static ssize_t sfp_eeprom_read(struct i2c_client *, u8, u8 *,int); +static ssize_t sfp_eeprom_write(struct i2c_client *, u8 , const char *,int); +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { SFP_EEPROM_A0_I2C_ADDR, SFP_EEPROM_A2_I2C_ADDR, I2C_CLIENT_END }; + +#define CPLD_PORT_TO_FRONT_PORT(port) (port+1) + +enum port_numbers { +sfp1, sfp2, sfp3, sfp4, sfp5, sfp6, sfp7, sfp8, +sfp9, sfp10, sfp11, sfp12, sfp13, sfp14, sfp15, sfp16, +sfp17, sfp18, sfp19, sfp20, sfp21, sfp22, sfp23, sfp24, +sfp25, sfp26, sfp27, sfp28, sfp29, sfp30, sfp31, sfp32 +}; + +static const struct i2c_device_id sfp_device_id[] = { +{ "sfp1", sfp1 }, { "sfp2", sfp2 }, { "sfp3", sfp3 }, { "sfp4", sfp4 }, +{ "sfp5", sfp5 }, { "sfp6", sfp6 }, { "sfp7", sfp7 }, { "sfp8", sfp8 }, +{ "sfp9", sfp9 }, { "sfp10", sfp10 }, { "sfp11", sfp11 }, { "sfp12", sfp12 }, +{ "sfp13", sfp13 }, { "sfp14", sfp14 }, { "sfp15", sfp15 }, { "sfp16", sfp16 }, +{ "sfp17", sfp17 }, { "sfp18", sfp18 }, { "sfp19", sfp19 }, { "sfp20", sfp20 }, +{ "sfp21", sfp21 }, { "sfp22", sfp22 }, { "sfp23", sfp23 }, { "sfp24", sfp24 }, +{ "sfp25", sfp25 }, { "sfp26", sfp26 }, { "sfp27", sfp27 }, { "sfp28", sfp28 }, +{ "sfp29", sfp29 }, { "sfp30", sfp30 }, { "sfp31", sfp31 }, { "sfp32", sfp32 }, +{} +}; +MODULE_DEVICE_TABLE(i2c, sfp_device_id); + +/* + * list of valid port types + * note OOM_PORT_TYPE_NOT_PRESENT to indicate no + * module is present in this port + */ +typedef enum oom_driver_port_type_e { + OOM_DRIVER_PORT_TYPE_INVALID, + OOM_DRIVER_PORT_TYPE_NOT_PRESENT, + OOM_DRIVER_PORT_TYPE_SFP, + OOM_DRIVER_PORT_TYPE_SFP_PLUS, + OOM_DRIVER_PORT_TYPE_QSFP, + OOM_DRIVER_PORT_TYPE_QSFP_PLUS, + OOM_DRIVER_PORT_TYPE_QSFP28 +} oom_driver_port_type_t; + +enum driver_type_e { + DRIVER_TYPE_SFP_MSA, + DRIVER_TYPE_SFP_DDM, + DRIVER_TYPE_QSFP +}; + +/* Each client has this additional data + */ +struct eeprom_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + struct bin_attribute bin; /* eeprom data */ +}; + +struct sfp_msa_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u64 status[2]; /* index 0 => device id + 1 => 10G Ethernet Compliance Codes + to distinguish SFP or SFP+ + 2 => DIAGNOSTIC MONITORING TYPE */ + struct eeprom_data eeprom; +}; + +struct sfp_ddm_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u64 status[3]; /* bit0:port0, bit1:port1 and so on */ + /* index 0 => tx_fail + 1 => tx_disable + 2 => rx_loss */ + struct eeprom_data eeprom; +}; + +struct qsfp_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 status[3]; /* bit0:port0, bit1:port1 and so on */ + /* index 0 => tx_fail + 1 => tx_disable + 2 => rx_loss */ + + u8 device_id; + struct eeprom_data eeprom; +}; + +struct sfp_port_data { + struct mutex update_lock; + enum driver_type_e driver_type; + int port; /* CPLD port index */ + oom_driver_port_type_t port_type; + u64 present; /* present status, bit0:port0, bit1:port1 and so on */ + u64 port_reset; /* reset status, bit0:port0, bit1:port1 and so on */ + + struct sfp_msa_data *msa; + struct sfp_ddm_data *ddm; + struct qsfp_data *qsfp; + + struct i2c_client *client; +}; + +enum sfp_sysfs_attributes { + PRESENT, + PRESENT_ALL, + PORT_NUMBER, + PORT_TYPE, + DDM_IMPLEMENTED, + TX_FAULT, + TX_FAULT1, + TX_FAULT2, + TX_FAULT3, + TX_FAULT4, + TX_DISABLE, + TX_DISABLE1, + TX_DISABLE2, + TX_DISABLE3, + TX_DISABLE4, + TX_DISABLE_ALL, + RX_LOS, + RX_LOS1, + RX_LOS2, + RX_LOS3, + RX_LOS4, + RX_LOS_ALL, + PORT_RESET +}; + +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + return sprintf(buf, "%d\n", CPLD_PORT_TO_FRONT_PORT(data->port)); +} + +static struct sfp_port_data *sfp_update_present(struct i2c_client *client) +{ + struct sfp_port_data *data = i2c_get_clientdata(client); + int i = 0; + int status = -1; + u8 regs[] = {0x30, 0x31, 0x32, 0x33}; + + DEBUG_PRINT("Starting sfp present status update"); + mutex_lock(&data->update_lock); + + /* Read present status of port 1~32 */ + data->present = 0; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + status = accton_i2c_cpld_read(0x60, regs[i]); + + if (status < 0) { + DEBUG_PRINT("cpld(0x60) reg(0x%x) err %d", regs[i], status); + goto exit; + } + + DEBUG_PRINT("Present status = 0x%llx", data->present); + data->present |= (u64)status << (i*8); + } + + DEBUG_PRINT("Present status = 0x%llx", data->present); +exit: + mutex_unlock(&data->update_lock); + return data; +} + +static struct sfp_port_data *sfp_update_tx_rx_status(struct device *dev) +{ + return NULL; +} + +static ssize_t sfp_set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + return 0; +} + +static int sfp_is_port_present(struct i2c_client *client, int port) +{ + struct sfp_port_data *data = sfp_update_present(client); + return (data->present & BIT_INDEX(data->port)) ? 0 : 1; +} + +static ssize_t show_present(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + + if (PRESENT_ALL == attr->index) { + + } + + /* PRESENT */ + return sprintf(buf, "%d\n", sfp_is_port_present(client, data->port)); +} + +static struct sfp_port_data *sfp_update_port_type(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + u8 buf = 0; + int status; + + mutex_lock(&data->update_lock); + + switch (data->driver_type) { + case DRIVER_TYPE_SFP_MSA: + { + status = sfp_eeprom_read(data->client, SFF8024_PHYSICAL_DEVICE_ID_ADDR, &buf, sizeof(buf)); + if (status < 0) { + data->port_type = OOM_DRIVER_PORT_TYPE_INVALID; + break; + } + + if (buf != SFF8024_DEVICE_ID_SFP) { + data->port_type = OOM_DRIVER_PORT_TYPE_INVALID; + break; + } + + status = sfp_eeprom_read(data->client, SFF8472_10G_ETH_COMPLIANCE_ADDR, &buf, sizeof(buf)); + if (status < 0) { + data->port_type = OOM_DRIVER_PORT_TYPE_INVALID; + break; + } + + DEBUG_PRINT("sfp port type (0x3) data = (0x%x)", buf); + data->port_type = buf & SFF8472_10G_BASE_MASK ? OOM_DRIVER_PORT_TYPE_SFP_PLUS : OOM_DRIVER_PORT_TYPE_SFP; + break; + } + case DRIVER_TYPE_QSFP: + { + status = sfp_eeprom_read(data->client, SFF8024_PHYSICAL_DEVICE_ID_ADDR, &buf, sizeof(buf)); + if (status < 0) { + data->port_type = OOM_DRIVER_PORT_TYPE_INVALID; + break; + } + + DEBUG_PRINT("qsfp port type (0x0) buf = (0x%x)", buf); + switch (buf) { + case SFF8024_DEVICE_ID_QSFP: + data->port_type = OOM_DRIVER_PORT_TYPE_QSFP; + break; + case SFF8024_DEVICE_ID_QSFP_PLUS: + data->port_type = OOM_DRIVER_PORT_TYPE_QSFP_PLUS; + break; + case SFF8024_DEVICE_ID_QSFP28: + data->port_type = OOM_DRIVER_PORT_TYPE_QSFP_PLUS; + break; + default: + data->port_type = OOM_DRIVER_PORT_TYPE_INVALID; + break; + } + + break; + } + default: + break; + } + + mutex_unlock(&data->update_lock); + return data; +} + +static ssize_t show_port_type(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + + if (!sfp_is_port_present(client, data->port)) { + return sprintf(buf, "%d\n", OOM_DRIVER_PORT_TYPE_NOT_PRESENT); + } + + sfp_update_port_type(dev); + return sprintf(buf, "%d\n", data->port_type); +} + + +static struct sfp_port_data *sfp_update_port_reset(struct i2c_client *client) +{ + struct sfp_port_data *data = i2c_get_clientdata(client); + int i = 0; + int status = -1; + u8 regs[] = {0x4, 0x5, 0x6, 0x7}; + + mutex_lock(&data->update_lock); + + /* Read reset status of port 1~32 */ + data->port_reset = 0; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + status = accton_i2c_cpld_read(0x60, regs[i]); + + if (status < 0) { + DEBUG_PRINT("cpld(0x60) reg(0x%x) err %d", regs[i], status); + goto exit; + } + + DEBUG_PRINT("reset status = 0x%x", status); + data->port_reset |= (u64)status << (i*8); + } + + DEBUG_PRINT("reset status = 0x%llx", data->port_reset); +exit: + mutex_unlock(&data->update_lock); + return data; +} + +static ssize_t show_port_reset(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + int is_reset = 0; + + if (!sfp_is_port_present(client, data->port)) { + return sprintf(buf, "%d\n", OOM_DRIVER_PORT_TYPE_NOT_PRESENT); + } + + sfp_update_port_reset(client); + is_reset = (data->port_reset & BIT_INDEX(data->port))? 0 : 1; + + return sprintf(buf, "%d\n", is_reset); +} + +static ssize_t sfp_set_port_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + u8 cpld_reg = 0, cpld_val = 0, cpld_bit = 0; + long is_reset; + int error; + + error = kstrtol(buf, 10, &is_reset); + if (error) { + return error; + } + + mutex_lock(&data->update_lock); + + cpld_reg = 0x4 + data->port / 8; + cpld_bit = 1 << (data->port % 8); + + cpld_val = accton_i2c_cpld_read(0x60, cpld_reg); + DEBUG_PRINT("current cpld reg = 0x%x value = 0x%x", cpld_reg, cpld_val); + + /* Update reset status. CPLD defined 0 is reset state, 1 is normal state. + * is_reset: 0 is not reset. 1 is reset. + */ + if (is_reset == 0) { + data->port_reset |= BIT_INDEX(data->port); + cpld_val |= cpld_bit; + } + else { + data->port_reset &= ~BIT_INDEX(data->port); + cpld_val &= ~cpld_bit; + } + + accton_i2c_cpld_write(0x60, cpld_reg, cpld_val); + DEBUG_PRINT("write cpld reg = 0x%x value = 0x%x cpld_bit = 0x%x", cpld_reg, cpld_val,cpld_bit); + + mutex_unlock(&data->update_lock); + + return count; +} + +static struct sfp_port_data *qsfp_update_tx_rx_status(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + int i, status = -1; + u8 buf = 0; + u8 reg[] = {SFF8436_TX_FAULT_ADDR, SFF8436_TX_DISABLE_ADDR, SFF8436_RX_LOS_ADDR}; + + if (time_before(jiffies, data->qsfp->last_updated + HZ + HZ / 2) && data->qsfp->valid) { + return data; + } + + dev_dbg(dev, "Starting sfp tx rx status update"); + mutex_lock(&data->update_lock); + data->qsfp->valid = 0; + memset(data->qsfp->status, 0, sizeof(data->qsfp->status)); + + /* Notify device to update tx fault/ tx disable/ rx los status */ + for (i = 0; i < ARRAY_SIZE(reg); i++) { + status = sfp_eeprom_read(data->client, reg[i], &buf, sizeof(buf)); + if (status < 0) { + goto exit; + } + } + msleep(200); + + /* Read actual tx fault/ tx disable/ rx los status */ + for (i = 0; i < ARRAY_SIZE(reg); i++) { + status = sfp_eeprom_read(data->client, reg[i], &buf, sizeof(buf)); + if (status < 0) { + goto exit; + } + + DEBUG_PRINT("qsfp reg(0x%x) status = (0x%x)", reg[i], data->qsfp->status[i]); + data->qsfp->status[i] = (buf & 0xF); + } + + data->qsfp->valid = 1; + data->qsfp->last_updated = jiffies; + mutex_unlock(&data->update_lock); + return data; + +exit: + mutex_unlock(&data->update_lock); + return NULL; +} + +static ssize_t qsfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + u8 val = 0; + struct i2c_client *client = to_i2c_client(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct sfp_port_data *data = i2c_get_clientdata(client); + + if (!sfp_is_port_present(client, data->port)) { + return -ENODEV; + } + + data = qsfp_update_tx_rx_status(dev); + if (!data) { + return -EIO; + } + + switch (attr->index) { + case TX_FAULT1: + case TX_FAULT2: + case TX_FAULT3: + case TX_FAULT4: + val = (data->qsfp->status[2] & BIT_INDEX(attr->index - TX_FAULT1)) ? 1 : 0; + break; + case TX_DISABLE1: + case TX_DISABLE2: + case TX_DISABLE3: + case TX_DISABLE4: + val = (data->qsfp->status[1] & BIT_INDEX(attr->index - TX_DISABLE1)) ? 1 : 0; + break; + case TX_DISABLE_ALL: + val = ((data->qsfp->status[1] & 0xF) == 0xF) ? 1 : 0; + break; + + case RX_LOS1: + case RX_LOS2: + case RX_LOS3: + case RX_LOS4: + val = (data->qsfp->status[0] & BIT_INDEX(attr->index - RX_LOS1)) ? 1 : 0; + break; + default: + break; + } + + return sprintf(buf, "%d\n", val); +} + +static ssize_t qsfp_set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + long disable; + int result; + struct i2c_client *client = to_i2c_client(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct sfp_port_data *data; + + result = kstrtol(buf, 10, &disable); + if (result) { + return result; + } + + data = qsfp_update_tx_rx_status(dev); + if (!data) { + return -EIO; + } + + mutex_lock(&data->update_lock); + + DEBUG_PRINT ("disable:%ld %d==%d %u\r\n", disable, attr->index, TX_DISABLE_ALL, data->qsfp->status[1]); + + if (attr->index == TX_DISABLE_ALL) + { + data->qsfp->status[1] = disable? 0xF:0; + } + else + { + if (disable) { + data->qsfp->status[1] |= (1 << (attr->index - TX_DISABLE1)); + } + else { + data->qsfp->status[1] &= ~(1 << (attr->index - TX_DISABLE1)); + } + } + DEBUG_PRINT("index = (%d), status = (0x%x)", attr->index, data->qsfp->status[1]); + result = sfp_eeprom_write(client, SFF8436_TX_DISABLE_ADDR, &data->qsfp->status[1], sizeof(data->qsfp->status[1])); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t sfp_show_ddm_implemented(struct device *dev, struct device_attribute *da, + char *buf) +{ + int status; + char ddm; + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + + if (!sfp_is_port_present(client, data->port)) { + return -ENODEV; + } + + status = sfp_eeprom_read(data->client, SFF8472_DIAG_MON_TYPE_ADDR, &ddm, sizeof(ddm)); + if (status < 0) { + return -EIO; + } + + return sprintf(buf, "%d\n", (ddm & SFF8472_DIAG_MON_TYPE_DDM_MASK) ? 1 : 0); +} + +static ssize_t sfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + u8 val = 0, index = 0; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct sfp_port_data *data; + + data = sfp_update_tx_rx_status(dev); + if (!data) { + return -EIO; + } + + switch (attr->index) { + case TX_FAULT: + index = 0; + break; + case TX_DISABLE: + index = 1; + break; + case RX_LOS: + index = 2; + break; + default: + break; + } + + val = (data->ddm->status[index] & BIT_INDEX(data->port)) ? 1 : 0; + return sprintf(buf, "%d\n", val); +} + +/* SFP/QSFP common attributes for sysfs */ +static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, PORT_NUMBER); +static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_present, NULL, PRESENT); +static SENSOR_DEVICE_ATTR(sfp_port_type, S_IRUGO, show_port_type, NULL, PORT_TYPE); +static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_present, NULL, PRESENT); +static SENSOR_DEVICE_ATTR(sfp_port_reset, S_IWUSR | S_IRUGO, show_port_reset, sfp_set_port_reset, PORT_RESET); + +/* QSFP attributes for sysfs */ +static SENSOR_DEVICE_ATTR(sfp_rx_los1, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS1); +static SENSOR_DEVICE_ATTR(sfp_rx_los2, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS2); +static SENSOR_DEVICE_ATTR(sfp_rx_los3, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS3); +static SENSOR_DEVICE_ATTR(sfp_rx_los4, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS4); +static SENSOR_DEVICE_ATTR(sfp_tx_disable1, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE1); +static SENSOR_DEVICE_ATTR(sfp_tx_disable2, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE2); +static SENSOR_DEVICE_ATTR(sfp_tx_disable3, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE3); +static SENSOR_DEVICE_ATTR(sfp_tx_disable4, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE4); +static SENSOR_DEVICE_ATTR(sfp_tx_disable_all, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE_ALL); +static SENSOR_DEVICE_ATTR(sfp_tx_fault1, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT1); +static SENSOR_DEVICE_ATTR(sfp_tx_fault2, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT2); +static SENSOR_DEVICE_ATTR(sfp_tx_fault3, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT3); +static SENSOR_DEVICE_ATTR(sfp_tx_fault4, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT4); +static struct attribute *qsfp_attributes[] = { + &sensor_dev_attr_sfp_port_number.dev_attr.attr, + &sensor_dev_attr_sfp_port_type.dev_attr.attr, + &sensor_dev_attr_sfp_is_present.dev_attr.attr, + &sensor_dev_attr_sfp_port_reset.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los1.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los2.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los3.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los4.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable1.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable2.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable3.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable4.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable_all.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault1.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault2.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault3.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault4.dev_attr.attr, + NULL +}; + +/* SFP msa attributes for sysfs */ +static SENSOR_DEVICE_ATTR(sfp_ddm_implemented, S_IRUGO, sfp_show_ddm_implemented, NULL, DDM_IMPLEMENTED); +static struct attribute *sfp_msa_attributes[] = { + &sensor_dev_attr_sfp_port_number.dev_attr.attr, + &sensor_dev_attr_sfp_port_type.dev_attr.attr, + &sensor_dev_attr_sfp_is_present.dev_attr.attr, + &sensor_dev_attr_sfp_ddm_implemented.dev_attr.attr, + NULL +}; + +/* SFP ddm attributes for sysfs */ +static SENSOR_DEVICE_ATTR(sfp_rx_los, S_IRUGO, sfp_show_tx_rx_status, NULL, RX_LOS); +static SENSOR_DEVICE_ATTR(sfp_tx_disable, S_IWUSR | S_IRUGO, sfp_show_tx_rx_status, sfp_set_tx_disable, TX_DISABLE); +static SENSOR_DEVICE_ATTR(sfp_tx_fault, S_IRUGO, sfp_show_tx_rx_status, NULL, TX_FAULT); +static struct attribute *sfp_ddm_attributes[] = { + &sensor_dev_attr_sfp_tx_fault.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable.dev_attr.attr, + NULL +}; + +static ssize_t sfp_eeprom_write(struct i2c_client *client, u8 command, const char *data, + int data_len) +{ +#if USE_I2C_BLOCK_READ + int result, retry = I2C_RW_RETRY_COUNT; + + if (data_len > I2C_SMBUS_BLOCK_MAX) { + data_len = I2C_SMBUS_BLOCK_MAX; + } + + while (retry) { + result = i2c_smbus_write_i2c_block_data(client, command, data_len, data); + if (result < 0) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(result < 0)) { + return result; + } + + return data_len; +#else + int result, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + result = i2c_smbus_write_byte_data(client, command, *data); + if (result < 0) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(result < 0)) { + return result; + } + + return 1; +#endif + + +} + + +static ssize_t sfp_port_write(struct sfp_port_data *data, + 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(&data->update_lock); + + while (count) { + ssize_t status; + + status = sfp_eeprom_write(data->client, off, buf, count); + if (status <= 0) { + if (retval == 0) { + retval = status; + } + break; + } + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&data->update_lock); + return retval; +} + + +static ssize_t sfp_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct sfp_port_data *data; + DEBUG_PRINT("offset = (%d), count = (%d)", off, count); + data = dev_get_drvdata(container_of(kobj, struct device, kobj)); + return sfp_port_write(data, buf, off, count); +} + +static ssize_t sfp_eeprom_read(struct i2c_client *client, u8 command, u8 *data, + int data_len) +{ +#if USE_I2C_BLOCK_READ + int result, retry = I2C_RW_RETRY_COUNT; + + if (data_len > I2C_SMBUS_BLOCK_MAX) { + data_len = I2C_SMBUS_BLOCK_MAX; + } + + while (retry) { + result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); + if (result < 0) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(result < 0)) + goto abort; + if (unlikely(result != data_len)) { + result = -EIO; + goto abort; + } + + //result = data_len; + +abort: + return result; +#else + int result, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + result = i2c_smbus_read_byte_data(client, command); + if (result < 0) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(result < 0)) { + dev_dbg(&client->dev, "sfp read byte data failed, command(0x%2x), data(0x%2x)\r\n", command, result); + goto abort; + } + + *data = (u8)result; + result = 1; + +abort: + return result; +#endif +} + +static ssize_t sfp_port_read(struct sfp_port_data *data, + char *buf, loff_t off, size_t count) +{ + ssize_t retval = 0; + + if (unlikely(!count)) { + DEBUG_PRINT("Count = 0, return"); + return count; + } + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&data->update_lock); + + while (count) { + ssize_t status; + + status = sfp_eeprom_read(data->client, off, buf, count); + if (status <= 0) { + if (retval == 0) { + retval = status; + } + break; + } + + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&data->update_lock); + return retval; + +} + +static ssize_t sfp_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct sfp_port_data *data; + DEBUG_PRINT("offset = (%d), count = (%d)", off, count); + data = dev_get_drvdata(container_of(kobj, struct device, kobj)); + return sfp_port_read(data, buf, off, count); +} + +static int sfp_sysfs_eeprom_init(struct kobject *kobj, struct bin_attribute *eeprom) +{ + int err; + + sysfs_bin_attr_init(eeprom); + eeprom->attr.name = EEPROM_NAME; + eeprom->attr.mode = S_IWUSR | S_IRUGO; + eeprom->read = sfp_bin_read; + eeprom->write = sfp_bin_write; + eeprom->size = EEPROM_SIZE; + + /* Create eeprom file */ + err = sysfs_create_bin_file(kobj, eeprom); + if (err) { + return err; + } + + return 0; +} + +static int sfp_sysfs_eeprom_cleanup(struct kobject *kobj, struct bin_attribute *eeprom) +{ + sysfs_remove_bin_file(kobj, eeprom); + return 0; +} + +static const struct attribute_group sfp_msa_group = { + .attrs = sfp_msa_attributes, +}; + +static int sfp_i2c_check_functionality(struct i2c_client *client) +{ +#if USE_I2C_BLOCK_READ + return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK); +#else + return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA); +#endif +} + +static int sfp_msa_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, + struct sfp_msa_data **data) +{ + int status; + struct sfp_msa_data *msa; + + if (!sfp_i2c_check_functionality(client)) { + status = -EIO; + goto exit; + } + + msa = kzalloc(sizeof(struct sfp_msa_data), GFP_KERNEL); + if (!msa) { + status = -ENOMEM; + goto exit; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &sfp_msa_group); + if (status) { + goto exit_free; + } + + /* init eeprom */ + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &msa->eeprom.bin); + if (status) { + goto exit_remove; + } + + *data = msa; + dev_info(&client->dev, "sfp msa '%s'\n", client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &sfp_msa_group); +exit_free: + kfree(msa); +exit: + + return status; +} + +static const struct attribute_group sfp_ddm_group = { + .attrs = sfp_ddm_attributes, +}; + +static int sfp_ddm_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, + struct sfp_ddm_data **data) +{ + int status; + struct sfp_ddm_data *ddm; + + if (!sfp_i2c_check_functionality(client)) { + status = -EIO; + goto exit; + } + + ddm = kzalloc(sizeof(struct sfp_ddm_data), GFP_KERNEL); + if (!ddm) { + status = -ENOMEM; + goto exit; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &sfp_ddm_group); + if (status) { + goto exit_free; + } + + /* init eeprom */ + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &ddm->eeprom.bin); + if (status) { + goto exit_remove; + } + + *data = ddm; + dev_info(&client->dev, "sfp ddm '%s'\n", client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &sfp_ddm_group); +exit_free: + kfree(ddm); +exit: + + return status; +} + +static const struct attribute_group qsfp_group = { + .attrs = qsfp_attributes, +}; + +static int qsfp_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, + struct qsfp_data **data) +{ + int status; + struct qsfp_data *qsfp; + + if (!sfp_i2c_check_functionality(client)) { + status = -EIO; + goto exit; + } + + qsfp = kzalloc(sizeof(struct qsfp_data), GFP_KERNEL); + if (!qsfp) { + status = -ENOMEM; + goto exit; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &qsfp_group); + if (status) { + goto exit_free; + } + + /* init eeprom */ + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &qsfp->eeprom.bin); + if (status) { + goto exit_remove; + } + + /* Bring QSFPs out of reset + as6712_32x_i2c_cpld_write(0x62, 0x4, 0xFF); + as6712_32x_i2c_cpld_write(0x62, 0x5, 0xFF); + as6712_32x_i2c_cpld_write(0x64, 0x4, 0xFF); + as6712_32x_i2c_cpld_write(0x64, 0x5, 0xFF);*/ + + *data = qsfp; + dev_info(&client->dev, "qsfp '%s'\n", client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &qsfp_group); +exit_free: + kfree(qsfp); +exit: + + return status; +} + +static int sfp_device_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct sfp_port_data *data = NULL; + + data = kzalloc(sizeof(struct sfp_port_data), GFP_KERNEL); + if (!data) { + return -ENOMEM; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->port = dev_id->driver_data; + data->client = client; + + if (client->addr != SFP_EEPROM_A0_I2C_ADDR) { + return -ENODEV; + } + + data->driver_type = DRIVER_TYPE_QSFP; + return qsfp_probe(client, dev_id, &data->qsfp); +} + +static int sfp_msa_remove(struct i2c_client *client, struct sfp_msa_data *data) +{ + sfp_sysfs_eeprom_cleanup(&client->dev.kobj, &data->eeprom.bin); + sysfs_remove_group(&client->dev.kobj, &sfp_msa_group); + kfree(data); + return 0; +} + +static int sfp_ddm_remove(struct i2c_client *client, struct sfp_ddm_data *data) +{ + sfp_sysfs_eeprom_cleanup(&client->dev.kobj, &data->eeprom.bin); + sysfs_remove_group(&client->dev.kobj, &sfp_ddm_group); + kfree(data); + return 0; +} + +static int qfp_remove(struct i2c_client *client, struct qsfp_data *data) +{ + sfp_sysfs_eeprom_cleanup(&client->dev.kobj, &data->eeprom.bin); + sysfs_remove_group(&client->dev.kobj, &qsfp_group); + kfree(data); + return 0; +} + +static int sfp_device_remove(struct i2c_client *client) +{ + struct sfp_port_data *data = i2c_get_clientdata(client); + + switch (data->driver_type) { + case DRIVER_TYPE_SFP_MSA: + return sfp_msa_remove(client, data->msa); + case DRIVER_TYPE_SFP_DDM: + return sfp_ddm_remove(client, data->ddm); + case DRIVER_TYPE_QSFP: + return qfp_remove(client, data->qsfp); + } + + return 0; +} + +static struct i2c_driver sfp_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = sfp_device_probe, + .remove = sfp_device_remove, + .id_table = sfp_device_id, + .address_list = normal_i2c, +}; + +static int __init sfp_init(void) +{ + return i2c_add_driver(&sfp_driver); +} + +static void __exit sfp_exit(void) +{ + i2c_del_driver(&sfp_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton as7712_32x_sfp driver"); +MODULE_LICENSE("GPL"); + +module_init(sfp_init); +module_exit(sfp_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_i2c_cpld.c b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_i2c_cpld.c new file mode 120000 index 000000000000..39c0826d16fd --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_i2c_cpld.c @@ -0,0 +1 @@ +../../common/modules/accton_i2c_cpld.c \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_pmbus_3y.c b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_pmbus_3y.c new file mode 120000 index 000000000000..7504a0556e18 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/accton_pmbus_3y.c @@ -0,0 +1 @@ +../../common/modules/accton_pmbus_3y.c \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/leds-accton_as7712_32x.c b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/leds-accton_as7712_32x.c new file mode 100644 index 000000000000..5e1a9282db0e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/leds-accton_as7712_32x.c @@ -0,0 +1,690 @@ +/* + * A LED driver for the accton_as7712_32x_led + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * 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. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern int accton_i2c_cpld_read (unsigned short cpld_addr, u8 reg); +extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +extern void led_classdev_unregister(struct led_classdev *led_cdev); +extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev); +extern void led_classdev_resume(struct led_classdev *led_cdev); +extern void led_classdev_suspend(struct led_classdev *led_cdev); + +#define DRVNAME "accton_as7712_32x_led" +#define ENABLE_PORT_LED 1 + +struct accton_as7712_32x_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[1]; /* only 1 register*/ +}; + +static struct accton_as7712_32x_led_data *ledctl = NULL; + +/* LED related data + */ + +#define LED_CNTRLER_I2C_ADDRESS (0x60) + +#define LED_TYPE_DIAG_REG_MASK (0x3) +#define LED_MODE_DIAG_GREEN_VALUE (0x02) +#define LED_MODE_DIAG_RED_VALUE (0x01) +#define LED_MODE_DIAG_AMBER_VALUE (0x00) /*It's yellow actually. Green+Red=Yellow*/ +#define LED_MODE_DIAG_OFF_VALUE (0x03) + +#define LED_TYPE_LOC_REG_MASK (0x80) +#define LED_MODE_LOC_ON_VALUE (0) +#define LED_MODE_LOC_OFF_VALUE (0x80) + +#if (ENABLE_PORT_LED == 1) +#define LED_TYPE_PORT_LED(port) \ + LED_TYPE_PORT##port##_LED0, \ + LED_TYPE_PORT##port##_LED1, \ + LED_TYPE_PORT##port##_LED2, \ + LED_TYPE_PORT##port##_LED3 +#endif + +enum led_type { + LED_TYPE_DIAG, + LED_TYPE_LOC, + LED_TYPE_FAN, + LED_TYPE_PSU1, + LED_TYPE_PSU2, +#if (ENABLE_PORT_LED == 1) + LED_TYPE_PORT_LED(0), + LED_TYPE_PORT_LED(1), + LED_TYPE_PORT_LED(2), + LED_TYPE_PORT_LED(3), + LED_TYPE_PORT_LED(4), + LED_TYPE_PORT_LED(5), + LED_TYPE_PORT_LED(6), + LED_TYPE_PORT_LED(7), + LED_TYPE_PORT_LED(8), + LED_TYPE_PORT_LED(9), + LED_TYPE_PORT_LED(10), + LED_TYPE_PORT_LED(11), + LED_TYPE_PORT_LED(12), + LED_TYPE_PORT_LED(13), + LED_TYPE_PORT_LED(14), + LED_TYPE_PORT_LED(15), + LED_TYPE_PORT_LED(16), + LED_TYPE_PORT_LED(17), + LED_TYPE_PORT_LED(18), + LED_TYPE_PORT_LED(19), + LED_TYPE_PORT_LED(20), + LED_TYPE_PORT_LED(21), + LED_TYPE_PORT_LED(22), + LED_TYPE_PORT_LED(23), + LED_TYPE_PORT_LED(24), + LED_TYPE_PORT_LED(25), + LED_TYPE_PORT_LED(26), + LED_TYPE_PORT_LED(27), + LED_TYPE_PORT_LED(28), + LED_TYPE_PORT_LED(29), + LED_TYPE_PORT_LED(30), + LED_TYPE_PORT_LED(31), +#endif +}; + +struct led_reg { + u32 types; + u8 reg_addr; +}; + +static const struct led_reg led_reg_map[] = { + {(1<update_lock); + + if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) + || !ledctl->valid) { + int i; + + dev_dbg(&ledctl->pdev->dev, "Starting accton_as7712_32x_led update\n"); + + /* Update LED data + */ + for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { + int status = accton_as7712_32x_led_read_value(led_reg_map[i].reg_addr); + + if (status < 0) { + ledctl->valid = 0; + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg_map[i].reg_addr, status); + goto exit; + } + else + { + ledctl->reg_val[i] = status; + } + } + + ledctl->last_updated = jiffies; + ledctl->valid = 1; + } + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void accton_as7712_32x_led_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode, + enum led_type type) +{ + int reg_val; + u8 reg ; + mutex_lock(&ledctl->update_lock); + + if( !accton_getLedReg(type, ®)) + { + dev_dbg(&ledctl->pdev->dev, "Not match item for %d.\n", type); + } + + reg_val = accton_as7712_32x_led_read_value(reg); + + if (reg_val < 0) { + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", reg, reg_val); + goto exit; + } + reg_val = led_light_mode_to_reg_val(type, led_light_mode, reg_val); + accton_as7712_32x_led_write_value(reg, reg_val); + + /* to prevent the slow-update issue */ + ledctl->valid = 0; + +exit: + mutex_unlock(&ledctl->update_lock); +} + + +static void accton_as7712_32x_led_diag_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as7712_32x_led_set(led_cdev, led_light_mode, LED_TYPE_DIAG); +} + +static enum led_brightness accton_as7712_32x_led_diag_get(struct led_classdev *cdev) +{ + accton_as7712_32x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); +} + +static void accton_as7712_32x_led_loc_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as7712_32x_led_set(led_cdev, led_light_mode, LED_TYPE_LOC); +} + +static enum led_brightness accton_as7712_32x_led_loc_get(struct led_classdev *cdev) +{ + accton_as7712_32x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); +} + +static void accton_as7712_32x_led_auto_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ +} + +static enum led_brightness accton_as7712_32x_led_auto_get(struct led_classdev *cdev) +{ + return LED_MODE_AUTO; +} + +#if (ENABLE_PORT_LED == 1) +#define PORT_LED_COLOR_MASK (0x7 << 2) +#define PORT_LED_COLOR1_REG_VAL (0x0 << 2) +#define PORT_LED_COLOR2_REG_VAL (0x1 << 2) +#define PORT_LED_COLOR3_REG_VAL (0x2 << 2) +#define PORT_LED_COLOR4_REG_VAL (0x3 << 2) +#define PORT_LED_COLOR5_REG_VAL (0x4 << 2) +#define PORT_LED_COLOR6_REG_VAL (0x5 << 2) +#define PORT_LED_COLOR7_REG_VAL (0x6 << 2) +#define PORT_LED_COLOR8_REG_VAL (0x7 << 2) + +static int accton_as7712_32x_port_led_read_value(unsigned short cpld_addr, u8 reg) +{ + return accton_i2c_cpld_read(cpld_addr, reg); +} + +static int accton_as7712_32x_port_led_write_value(unsigned short cpld_addr, u8 reg, u8 value) +{ + return accton_i2c_cpld_write(cpld_addr, reg, value); +} + +static int port_led_mode_to_cpld_val(int mode) +{ + u8 color = 0; + u8 blinking = 0; + u8 on = 1 << 0; + + switch (mode) { + case LED_MODE_WHITE_BLINKING: blinking = 1 << 1; /* fall through */ + case LED_MODE_WHITE: color = 0x0 << 2; + break; + case LED_MODE_YELLOW_BLINKING: blinking = 1 << 1; /* fall through */ + case LED_MODE_YELLOW: color = 0x1 << 2; + break; + case LED_MODE_ORANGE_BLINKING: blinking = 1 << 1; /* fall through */ + case LED_MODE_ORANGE: color = 0x2 << 2; + break; + case LED_MODE_PURPLE_BLINKING: blinking = 1 << 1; /* fall through */ + case LED_MODE_PURPLE: color = 0x3 << 2; + break; + case LED_MODE_CYAN_BLINKING: blinking = 1 << 1; /* fall through */ + case LED_MODE_CYAN: color = 0x4 << 2; + break; + case LED_MODE_RED_BLINKING: blinking = 1 << 1; /* fall through */ + case LED_MODE_RED: color = 0x5 << 2; + break; + case LED_MODE_GREEN_BLINKING: blinking = 1 << 1; /* fall through */ + case LED_MODE_GREEN: color = 0x6 << 2; + break; + case LED_MODE_BLUE_BLINKING: blinking = 1 << 1; /* fall through */ + case LED_MODE_BLUE: color = 0x7 << 2; + break; + case LED_MODE_OFF: on = 0 << 0; + break; + default: + return -EINVAL; + } + + return (color | blinking | on); +} + +static int cpld_val_to_port_led_mode(uint8_t value) +{ + int on = (value & 0x1); + int blinking = (value & 0x2); + int color = (value & PORT_LED_COLOR_MASK) ; + + if (!on) { + return LED_MODE_OFF; + } + + switch (color) { + case PORT_LED_COLOR1_REG_VAL: + return blinking ? LED_MODE_WHITE_BLINKING : LED_MODE_WHITE; + case PORT_LED_COLOR2_REG_VAL: + return blinking ? LED_MODE_YELLOW_BLINKING : LED_MODE_YELLOW; + case PORT_LED_COLOR3_REG_VAL: + return blinking ? LED_MODE_ORANGE_BLINKING : LED_MODE_ORANGE; + case PORT_LED_COLOR4_REG_VAL: + return blinking ? LED_MODE_PURPLE_BLINKING : LED_MODE_PURPLE; + case PORT_LED_COLOR5_REG_VAL: + return blinking ? LED_MODE_CYAN_BLINKING : LED_MODE_CYAN; + case PORT_LED_COLOR6_REG_VAL: + return blinking ? LED_MODE_RED_BLINKING : LED_MODE_RED; + case PORT_LED_COLOR7_REG_VAL: + return blinking ? LED_MODE_GREEN_BLINKING : LED_MODE_GREEN; + case PORT_LED_COLOR8_REG_VAL: + return blinking ? LED_MODE_BLUE_BLINKING : LED_MODE_BLUE; + default: + return -EINVAL;; + } +} + + +static void accton_as7712_32x_port_led_set(struct led_classdev *cdev, + enum led_brightness led_light_mode) +{ + unsigned int port, lid; + unsigned short cpld_addr; + u8 reg, value; + sscanf(cdev->name, "accton_as7712_32x_led::port%u_led%u", &port, &lid); + + if (port > 32 || lid > 4) { + dev_dbg(&ledctl->pdev->dev, "Port(%u), Led_id(%u) not match\n", port, lid); + return; + } + + cpld_addr = (port < 16) ? 0x64 : 0x62; + reg = (0x50 + (port % 16) * 4 + lid); + value = port_led_mode_to_cpld_val(led_light_mode); + + if (value < 0) { + dev_dbg(&ledctl->pdev->dev, "Unknow port led mode(%d)\n", led_light_mode); + return; + } + + accton_as7712_32x_port_led_write_value(cpld_addr, reg, value); +} + +static enum led_brightness accton_as7712_32x_port_led_get(struct led_classdev *cdev) +{ + unsigned int port, lid; + unsigned short cpld_addr; + u8 reg, value; + sscanf(cdev->name, "accton_as7712_32x_led::port%u_led%u", &port, &lid); + + if (port > 32 || lid > 4) { + dev_dbg(&ledctl->pdev->dev, "Port(%u), Led_id(%u) not match\n", port, lid); + return -EINVAL; + } + + cpld_addr = (port < 16) ? 0x64 : 0x62; + reg = (0x50 + (port % 16) * 4 + lid); + value = accton_as7712_32x_port_led_read_value(cpld_addr, reg); + + if (value < 0) { + dev_dbg(&ledctl->pdev->dev, "Unable to read reg value from cpld(0x%x), reg(0x%x)\n", cpld_addr, reg); + return value; + } + + return cpld_val_to_port_led_mode(value); +} + +#define _PORT_LED_CLASSDEV(port, lid) \ + [LED_TYPE_PORT##port##_LED##lid] = { \ + .name = "accton_as7712_32x_led::port"#port"_led"#lid,\ + .default_trigger = "unused", \ + .brightness_set = accton_as7712_32x_port_led_set, \ + .brightness_get = accton_as7712_32x_port_led_get, \ + .max_brightness = LED_MODE_CYAN_BLINKING, \ + } + +#define PORT_LED_CLASSDEV(port) \ + _PORT_LED_CLASSDEV(port, 0),\ + _PORT_LED_CLASSDEV(port, 1),\ + _PORT_LED_CLASSDEV(port, 2),\ + _PORT_LED_CLASSDEV(port, 3) +#endif + +static struct led_classdev accton_as7712_32x_leds[] = { + [LED_TYPE_DIAG] = { + .name = "accton_as7712_32x_led::diag", + .default_trigger = "unused", + .brightness_set = accton_as7712_32x_led_diag_set, + .brightness_get = accton_as7712_32x_led_diag_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_RED, + }, + [LED_TYPE_LOC] = { + .name = "accton_as7712_32x_led::loc", + .default_trigger = "unused", + .brightness_set = accton_as7712_32x_led_loc_set, + .brightness_get = accton_as7712_32x_led_loc_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_BLUE, + }, + [LED_TYPE_FAN] = { + .name = "accton_as7712_32x_led::fan", + .default_trigger = "unused", + .brightness_set = accton_as7712_32x_led_auto_set, + .brightness_get = accton_as7712_32x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU1] = { + .name = "accton_as7712_32x_led::psu1", + .default_trigger = "unused", + .brightness_set = accton_as7712_32x_led_auto_set, + .brightness_get = accton_as7712_32x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU2] = { + .name = "accton_as7712_32x_led::psu2", + .default_trigger = "unused", + .brightness_set = accton_as7712_32x_led_auto_set, + .brightness_get = accton_as7712_32x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, +#if (ENABLE_PORT_LED == 1) + PORT_LED_CLASSDEV(0), + PORT_LED_CLASSDEV(1), + PORT_LED_CLASSDEV(2), + PORT_LED_CLASSDEV(3), + PORT_LED_CLASSDEV(4), + PORT_LED_CLASSDEV(5), + PORT_LED_CLASSDEV(6), + PORT_LED_CLASSDEV(7), + PORT_LED_CLASSDEV(8), + PORT_LED_CLASSDEV(9), + PORT_LED_CLASSDEV(10), + PORT_LED_CLASSDEV(11), + PORT_LED_CLASSDEV(12), + PORT_LED_CLASSDEV(13), + PORT_LED_CLASSDEV(14), + PORT_LED_CLASSDEV(15), + PORT_LED_CLASSDEV(16), + PORT_LED_CLASSDEV(17), + PORT_LED_CLASSDEV(18), + PORT_LED_CLASSDEV(19), + PORT_LED_CLASSDEV(20), + PORT_LED_CLASSDEV(21), + PORT_LED_CLASSDEV(22), + PORT_LED_CLASSDEV(23), + PORT_LED_CLASSDEV(24), + PORT_LED_CLASSDEV(25), + PORT_LED_CLASSDEV(26), + PORT_LED_CLASSDEV(27), + PORT_LED_CLASSDEV(28), + PORT_LED_CLASSDEV(29), + PORT_LED_CLASSDEV(30), + PORT_LED_CLASSDEV(31), +#endif +}; + +static int accton_as7712_32x_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as7712_32x_leds); i++) { + led_classdev_suspend(&accton_as7712_32x_leds[i]); + } + + return 0; +} + +static int accton_as7712_32x_led_resume(struct platform_device *dev) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as7712_32x_leds); i++) { + led_classdev_resume(&accton_as7712_32x_leds[i]); + } + + return 0; +} + +static int accton_as7712_32x_led_probe(struct platform_device *pdev) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(accton_as7712_32x_leds); i++) { + ret = led_classdev_register(&pdev->dev, &accton_as7712_32x_leds[i]); + + if (ret < 0) + break; + } + + /* Check if all LEDs were successfully registered */ + if (i != ARRAY_SIZE(accton_as7712_32x_leds)){ + int j; + + /* only unregister the LEDs that were successfully registered */ + for (j = 0; j < i; j++) { + led_classdev_unregister(&accton_as7712_32x_leds[i]); + } + } + + return ret; +} + +static int accton_as7712_32x_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(accton_as7712_32x_leds); i++) { + led_classdev_unregister(&accton_as7712_32x_leds[i]); + } + + return 0; +} + +static struct platform_driver accton_as7712_32x_led_driver = { + .probe = accton_as7712_32x_led_probe, + .remove = accton_as7712_32x_led_remove, + .suspend = accton_as7712_32x_led_suspend, + .resume = accton_as7712_32x_led_resume, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init accton_as7712_32x_led_init(void) +{ + int ret; + + ret = platform_driver_register(&accton_as7712_32x_led_driver); + if (ret < 0) { + goto exit; + } + + ledctl = kzalloc(sizeof(struct accton_as7712_32x_led_data), GFP_KERNEL); + if (!ledctl) { + ret = -ENOMEM; + platform_driver_unregister(&accton_as7712_32x_led_driver); + goto exit; + } + + mutex_init(&ledctl->update_lock); + + ledctl->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(ledctl->pdev)) { + ret = PTR_ERR(ledctl->pdev); + platform_driver_unregister(&accton_as7712_32x_led_driver); + kfree(ledctl); + goto exit; + } + +exit: + return ret; +} + +static void __exit accton_as7712_32x_led_exit(void) +{ + platform_device_unregister(ledctl->pdev); + platform_driver_unregister(&accton_as7712_32x_led_driver); + kfree(ledctl); +} + +module_init(accton_as7712_32x_led_init); +module_exit(accton_as7712_32x_led_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_as7712_32x_led driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/pmbus.h b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/pmbus.h new file mode 120000 index 000000000000..b0e1a57107fd --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/pmbus.h @@ -0,0 +1 @@ +../../common/modules/pmbus.h \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/ym2651y.c b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/ym2651y.c new file mode 120000 index 000000000000..f4d67640ccc3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/modules/ym2651y.c @@ -0,0 +1 @@ +../../common/modules/ym2651y.c \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/service/as7712-platform-init.service b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/service/as7712-platform-init.service new file mode 100755 index 000000000000..d5d25b6cde04 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/service/as7712-platform-init.service @@ -0,0 +1,13 @@ +[Unit] +Description=Accton AS7712-32X Platform initialization service +Before=pmon.service +DefaultDependencies=no + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/accton_as7712_util.py install +ExecStop=/usr/local/bin/accton_as7712_util.py clean +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/setup.py b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/setup.py new file mode 100755 index 000000000000..09c763c84f01 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/setup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +import os +import sys +from setuptools import setup +os.listdir + +setup( + name='as7712_32x', + version='1.0', + description='Module to initialize Accton AS7712-32X platforms', + + packages=['as7712_32x'], + package_dir={'as7712_32x': 'as7712-32x/classes'}, +) + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/utils/README b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/utils/README new file mode 100755 index 000000000000..0b9fc1633999 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/utils/README @@ -0,0 +1,60 @@ +Copyright (C) 2016 Accton Networks, Inc. + +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 3 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, see . + +To initialize the system, run "accton_as7712_util.py install". +To clean up the drivers & devices, run "accton_as7712_util.py clean". +To dump information of sensors, run "accton_as7712_util.py show". +To dump SFP EEPROM, run "accton_as7712_util.py sff". +To set fan speed, run "accton_as7712_util.py set fan". +To enable/disable SFP emission, run "accton_as7712_util.py set sfp". +To set system LEDs' color, run "accton_as7712_util.py set led" +For more information, run "accton_as7712_util.py --help". + +==================================================================== +Besides applying accton_as7712_util.py to access peripherals, you can +access peripherals by sysfs nodes directly after the installation is run. + +LED controls can be found under /sys/class/leds. The sysfs interface +color mappings are as follows: +Brightness: + 0 => off + 1 => green + 2 => amber + 3 => red + 4 => blue + +There are 5 system LEDs, loc, diag, fan, ps1, and ps2. +They are lit automatically by CPLD, but the loc and diag. +The loc led has only 1 color, blue. +The diag one has 3 colors: red, amber, and green. + +Fan controls can be found in /sys/bus/i2c/devices/2-0066. +There are 12 fans inside 6 fan modules. +All fans share 1 duty setting, ranged from 0~100. + +Three temperature sensors are controlled by the lm75 kernel modules. +They should already be visible under /sys/bus/i2c/drivers/lm75/. + +Two power supplies are controlled by the CPLD. +Here provide their status under +/sys/bus/i2c/devices/10-0050 and /sys/bus/i2c/devices/11-0053. + +There are 32 QSFP+ modules are equipped. +Apply "accton_as7712_util.py show" to get their status. +Apply "accton_as7712_util.py set sfp" to turn on/off light transmission. +Apply "accton_as7712_util.py sff" to dump EEPROM information. +Before operating on that QSFP+, please make sure it is well plugged. +Otherwise, operation is going to fail. + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7712-32x/utils/accton_as7712_util.py b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/utils/accton_as7712_util.py new file mode 100755 index 000000000000..c8b755b85b69 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7712-32x/utils/accton_as7712_util.py @@ -0,0 +1,569 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 Accton Networks, Inc. +# +# 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 3 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, see . + +""" +Usage: %(scriptName)s [options] command object + +options: + -h | --help : this help message + -d | --debug : run with debug mode + -f | --force : ignore error during installation or clean +command: + install : install drivers and generate related sysfs nodes + clean : uninstall drivers and remove related sysfs nodes + show : show all systen status + sff : dump SFP eeprom + set : change board setting with fan|led|sfp +""" + +import os +import commands +import sys, getopt +import logging +import re +import time +from collections import namedtuple + + + + +PROJECT_NAME = 'as7712_32x' +version = '0.1.0' +verbose = False +DEBUG = False +args = [] +ALL_DEVICE = {} +DEVICE_NO = {'led':5, 'fan':6,'thermal':4, 'psu':2, 'sfp':32} +FORCE = 0 +#logging.basicConfig(filename= PROJECT_NAME+'.log', filemode='w',level=logging.DEBUG) +#logging.basicConfig(level=logging.INFO) + + +if DEBUG == True: + print sys.argv[0] + print 'ARGV :', sys.argv[1:] + + +def main(): + global DEBUG + global args + global FORCE + + if len(sys.argv)<2: + show_help() + + options, args = getopt.getopt(sys.argv[1:], 'hdf', ['help', + 'debug', + 'force', + ]) + if DEBUG == True: + print options + print args + print len(sys.argv) + + for opt, arg in options: + if opt in ('-h', '--help'): + show_help() + elif opt in ('-d', '--debug'): + DEBUG = True + logging.basicConfig(level=logging.INFO) + elif opt in ('-f', '--force'): + FORCE = 1 + else: + logging.info('no option') + for arg in args: + if arg == 'install': + do_install() + elif arg == 'clean': + do_uninstall() + elif arg == 'show': + device_traversal() + elif arg == 'sff': + if len(args)!=2: + show_eeprom_help() + elif int(args[1]) ==0 or int(args[1]) > DEVICE_NO['sfp']: + show_eeprom_help() + else: + show_eeprom(args[1]) + return + elif arg == 'set': + if len(args)<3: + show_set_help() + else: + set_device(args[1:]) + return + else: + show_help() + + + return 0 + +def show_help(): + print __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]} + sys.exit(0) + +def show_set_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print cmd +" [led|sfp|fan]" + print " use \""+ cmd + " led 0-4 \" to set led color" + print " use \""+ cmd + " fan 0-100\" to set fan duty percetage" + print " use \""+ cmd + " sfp 1-32 {0|1}\" to set sfp# tx_disable" + sys.exit(0) + +def show_eeprom_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print " use \""+ cmd + " 1-32 \" to dump sfp# eeprom" + sys.exit(0) + +def my_log(txt): + if DEBUG == True: + print "[ROY]"+txt + return + +def log_os_system(cmd, show): + logging.info('Run :'+cmd) + status, output = commands.getstatusoutput(cmd) + my_log (cmd +"with result:" + str(status)) + my_log (" output:"+output) + if status: + logging.info('Failed :'+cmd) + if show: + print('Failed :'+cmd) + return status, output + +def driver_check(): + ret, lsmod = log_os_system("lsmod| grep accton", 0) + logging.info('mods:'+lsmod) + if len(lsmod) ==0: + return False + return True + + + +kos = [ +'modprobe i2c_dev', +'modprobe i2c_mux_pca954x force_deselect_on_exit=1', +'modprobe accton_i2c_cpld' , +'modprobe ym2651y' , +'modprobe accton_as7712_32x_fan' , +'modprobe optoe' , +'modprobe leds-accton_as7712_32x' , +'modprobe accton_as7712_32x_psu' ] + +def driver_install(): + global FORCE + status, output = log_os_system("depmod", 1) + for i in range(0,len(kos)): + status, output = log_os_system(kos[i], 1) + if status: + if FORCE == 0: + return status + return 0 + +def driver_uninstall(): + global FORCE + for i in range(0,len(kos)): + rm = kos[-(i+1)].replace("modprobe", "modprobe -rq") + rm = rm.replace("insmod", "rmmod") + status, output = log_os_system(rm, 1) + if status: + if FORCE == 0: + return status + return 0 + +led_prefix ='/sys/class/leds/accton_'+PROJECT_NAME+'_led::' +hwmon_types = {'led': ['diag','fan','loc','psu1','psu2']} +hwmon_nodes = {'led': ['brightness'] } +hwmon_prefix ={'led': led_prefix} + +i2c_prefix = '/sys/bus/i2c/devices/' +i2c_bus = {'fan': ['2-0066'] , + 'thermal': ['3-0048','3-0049', '3-004a', '3-004b'] , + 'psu': ['10-0050','11-0053'], + 'sfp': ['-0050']} +i2c_nodes = {'fan': ['present', 'front_speed_rpm', 'rear_speed_rpm'] , + 'thermal': ['hwmon/hwmon*/temp1_input'] , + 'psu': ['psu_present ', 'psu_power_good'] , + 'sfp': ['module_present', 'sfp_tx_disable_all']} + +sfp_map = [22,23,24,25,27,26,29,28, + 18,19,20,21,30,31,32,33, + 34,35,36,37,46,47,48,49, + 38,39,40,41,42,43,44,45] +mknod =[ +'echo pca9548 0x76 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-1/new_device' , +'echo pca9548 0x72 > /sys/bus/i2c/devices/i2c-1/new_device' , +'echo pca9548 0x73 > /sys/bus/i2c/devices/i2c-1/new_device' , +'echo pca9548 0x74 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x75 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo 24c02 0x57 > /sys/bus/i2c/devices/i2c-1/new_device', + +'echo as7712_32x_fan 0x66 > /sys/bus/i2c/devices/i2c-2/new_device ', +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-3/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-3/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-3/new_device', +'echo lm75 0x4b > /sys/bus/i2c/devices/i2c-3/new_device', +'echo as7712_32x_psu1 0x53 > /sys/bus/i2c/devices/i2c-11/new_device', +'echo ym2651 0x5b > /sys/bus/i2c/devices/i2c-11/new_device', +'echo as7712_32x_psu2 0x50 > /sys/bus/i2c/devices/i2c-10/new_device', +'echo ym2651 0x58 > /sys/bus/i2c/devices/i2c-10/new_device', +'echo cpld_as7712 0x60 > /sys/bus/i2c/devices/i2c-4/new_device', +'echo cpld_plain 0x62 > /sys/bus/i2c/devices/i2c-5/new_device', +'echo cpld_plain 0x64 > /sys/bus/i2c/devices/i2c-6/new_device'] + +mknod2 =[ +'echo pca9548 0x76 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-0/new_device' , +'echo pca9548 0x72 > /sys/bus/i2c/devices/i2c-0/new_device' , +'echo pca9548 0X73 > /sys/bus/i2c/devices/i2c-0/new_device' , +'echo pca9548 0x74 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo pca9548 0x75 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo 24c02 0x57 > /sys/bus/i2c/devices/i2c-0/new_device', + +'echo as7712_32x_fan 0x66 > /sys/bus/i2c/devices/i2c-2/new_device ', +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-3/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-3/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-3/new_device', +'echo lm75 0x4b > /sys/bus/i2c/devices/i2c-3/new_device', +'echo as7712_32x_psu1 0x53 > /sys/bus/i2c/devices/i2c-11/new_device', +'echo ym2651 0x5b > /sys/bus/i2c/devices/i2c-11/new_device', +'echo as7712_32x_psu2 0x50 > /sys/bus/i2c/devices/i2c-10/new_device', +'echo ym2651 0x58 > /sys/bus/i2c/devices/i2c-10/new_device', +'echo cpld_as7712 0x60 > /sys/bus/i2c/devices/i2c-4/new_device', +'echo cpld_plain 0x62 > /sys/bus/i2c/devices/i2c-5/new_device', +'echo cpld_plain 0x64 > /sys/bus/i2c/devices/i2c-6/new_device'] + + +def i2c_order_check(): + # i2c bus 0 and 1 might be installed in different order. + # Here check if 0x76 is exist @ i2c-0 + tmp = "echo pca9548 0x76 > /sys/bus/i2c/devices/i2c-0/new_device" + status, output = log_os_system(tmp, 0) + if not device_exist(): + order = 1 + else: + order = 0 + tmp = "echo 0x76 > /sys/bus/i2c/devices/i2c-0/delete_device" + status, output = log_os_system(tmp, 0) + return order + +def device_install(): + global FORCE + + order = i2c_order_check() + + # if 0x76 is not exist @i2c-0, use reversed bus order + if order: + for i in range(0,len(mknod2)): + #for pca954x need times to built new i2c buses + if mknod2[i].find('pca954') != -1: + time.sleep(1) + + status, output = log_os_system(mknod2[i], 1) + if status: + print output + if FORCE == 0: + return status + else: + for i in range(0,len(mknod)): + #for pca954x need times to built new i2c buses + if mknod[i].find('pca954') != -1: + time.sleep(1) + + status, output = log_os_system(mknod[i], 1) + if status: + print output + if FORCE == 0: + return status + for i in range(0,len(sfp_map)): + status, output =log_os_system("echo optoe1 0x50 > /sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/new_device", 1) + if status: + print output + if FORCE == 0: + return status + return + +def device_uninstall(): + global FORCE + + status, output =log_os_system("ls /sys/bus/i2c/devices/1-0076", 0) + if status==0: + I2C_ORDER=1 + else: + I2C_ORDER=0 + + for i in range(0,len(sfp_map)): + target = "/sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/delete_device" + status, output =log_os_system("echo 0x50 > "+ target, 1) + if status: + print output + if FORCE == 0: + return status + + if I2C_ORDER==0: + nodelist = mknod + else: + nodelist = mknod2 + + for i in range(len(nodelist)): + target = nodelist[-(i+1)] + temp = target.split() + del temp[1] + temp[-1] = temp[-1].replace('new_device', 'delete_device') + status, output = log_os_system(" ".join(temp), 1) + if status: + print output + if FORCE == 0: + return status + + return + +def system_ready(): + if driver_check() == False: + return False + if not device_exist(): + return False + return True + +def do_install(): + print "Checking system...." + if driver_check() == False: + print "No driver, installing...." + status = driver_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" drivers detected...." + if not device_exist(): + print "No device, installing...." + status = device_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" devices detected...." + return + +def do_uninstall(): + print "Checking system...." + if not device_exist(): + print PROJECT_NAME.upper() +" has no device installed...." + else: + print "Removing device...." + status = device_uninstall() + if status: + if FORCE == 0: + return status + + if driver_check()== False : + print PROJECT_NAME.upper() +" has no driver installed...." + else: + print "Removing installed driver...." + status = driver_uninstall() + if status: + if FORCE == 0: + return status + + return + +def devices_info(): + global DEVICE_NO + global ALL_DEVICE + global i2c_bus, hwmon_types + for key in DEVICE_NO: + ALL_DEVICE[key]= {} + for i in range(0,DEVICE_NO[key]): + ALL_DEVICE[key][key+str(i+1)] = [] + + for key in i2c_bus: + buses = i2c_bus[key] + nodes = i2c_nodes[key] + for i in range(0,len(buses)): + for j in range(0,len(nodes)): + if 'fan' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ buses[i]+"/fan"+str(k+1)+"_"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + elif 'sfp' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ str(sfp_map[k])+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + else: + node = key+str(i+1) + path = i2c_prefix+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + + for key in hwmon_types: + itypes = hwmon_types[key] + nodes = hwmon_nodes[key] + for i in range(0,len(itypes)): + for j in range(0,len(nodes)): + node = key+"_"+itypes[i] + path = hwmon_prefix[key]+ itypes[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][ key+str(i+1)].append(path) + + #show dict all in the order + if DEBUG == True: + for i in sorted(ALL_DEVICE.keys()): + print(i+": ") + for j in sorted(ALL_DEVICE[i].keys()): + print(" "+j) + for k in (ALL_DEVICE[i][j]): + print(" "+" "+k) + return + +def show_eeprom(index): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + node = ALL_DEVICE['sfp'] ['sfp'+str(index)][0] + node = node.replace(node.split("/")[-1], 'sfp_eeprom') + # check if got hexdump command in current environment + ret, log = log_os_system("which hexdump", 0) + ret, log2 = log_os_system("which busybox hexdump", 0) + if len(log): + hex_cmd = 'hexdump' + elif len(log2): + hex_cmd = ' busybox hexdump' + else: + log = 'Failed : no hexdump cmd!!' + logging.info(log) + print log + return 1 + + print node + ":" + ret, log = log_os_system("cat "+node+"| "+hex_cmd+" -C", 1) + if ret==0: + print log + else: + print "**********device no found**********" + return + +def set_device(args): + global DEVICE_NO + global ALL_DEVICE + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + + if args[0]=='led': + if int(args[1])>4: + show_set_help() + return + #print ALL_DEVICE['led'] + for i in range(0,len(ALL_DEVICE['led'])): + for k in (ALL_DEVICE['led']['led'+str(i+1)]): + ret, log = log_os_system("echo "+args[1]+" >"+k, 1) + if ret: + return ret + elif args[0]=='fan': + if int(args[1])>100: + show_set_help() + return + #print ALL_DEVICE['fan'] + #fan1~6 is all fine, all fan share same setting + node = ALL_DEVICE['fan'] ['fan1'][0] + node = node.replace(node.split("/")[-1], 'fan_duty_cycle_percentage') + ret, log = log_os_system("cat "+ node, 1) + if ret==0: + print ("Previous fan duty: " + log.strip() +"%") + ret, log = log_os_system("echo "+args[1]+" >"+node, 1) + if ret==0: + print ("Current fan duty: " + args[1] +"%") + return ret + elif args[0]=='sfp': + if int(args[1])> DEVICE_NO[args[0]] or int(args[1])==0: + show_set_help() + return + if len(args)<2: + show_set_help() + return + + if int(args[2])>1: + show_set_help() + return + + #print ALL_DEVICE[args[0]] + for i in range(0,len(ALL_DEVICE[args[0]])): + for j in ALL_DEVICE[args[0]][args[0]+str(args[1])]: + if j.find('tx_disable')!= -1: + ret, log = log_os_system("echo "+args[2]+" >"+ j, 1) + if ret: + return ret + + return + +#get digits inside a string. +#Ex: 31 for "sfp31" +def get_value(input): + digit = re.findall('\d+', input) + return int(digit[0]) + +def device_traversal(): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + for i in sorted(ALL_DEVICE.keys()): + print("============================================") + print(i.upper()+": ") + print("============================================") + + for j in sorted(ALL_DEVICE[i].keys(), key=get_value): + print " "+j+":", + for k in (ALL_DEVICE[i][j]): + ret, log = log_os_system("cat "+k, 0) + func = k.split("/")[-1].strip() + func = re.sub(j+'_','',func,1) + func = re.sub(i.lower()+'_','',func,1) + if ret==0: + print func+"="+log+" ", + else: + print func+"="+"X"+" ", + print + print("----------------------------------------------------------------") + + + print + return + +def device_exist(): + ret1, log = log_os_system("ls "+i2c_prefix+"*0076", 0) + ret2, log = log_os_system("ls "+i2c_prefix+"i2c-2", 0) + return not(ret1 or ret2) + +if __name__ == "__main__": + main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/classes/__init__.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/classes/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/classes/fanutil.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/classes/fanutil.py new file mode 100755 index 000000000000..9a69f6e1d537 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/classes/fanutil.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# 1/10/2018: Jostar modify for as7716_32 +# ------------------------------------------------------------------ + +try: + import time + import logging + from collections import namedtuple +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + + +class FanUtil(object): + """Platform-specific FanUtil class""" + + FAN_NUM_ON_MAIN_BROAD = 6 + FAN_NUM_1_IDX = 1 + FAN_NUM_2_IDX = 2 + FAN_NUM_3_IDX = 3 + FAN_NUM_4_IDX = 4 + FAN_NUM_5_IDX = 5 + FAN_NUM_6_IDX = 6 + + FAN_NODE_NUM_OF_MAP = 2 + FAN_NODE_FAULT_IDX_OF_MAP = 1 + #FAN_NODE_SPEED_IDX_OF_MAP = 2 + FAN_NODE_DIR_IDX_OF_MAP = 2 + #FAN_NODE_DUTY_IDX_OF_MAP = 4 + #FANR_NODE_FAULT_IDX_OF_MAP = 5 + + #BASE_VAL_PATH = '/sys/devices/platform/as5712_54x_fan/{0}' + BASE_VAL_PATH = '/sys/bus/i2c/devices/9-0066/{0}' + FAN_DUTY_PATH = '/sys/bus/i2c/devices/9-0066/fan_duty_cycle_percentage' + + #logfile = '' + #loglevel = logging.INFO + + """ Dictionary where + key1 = fan id index (integer) starting from 1 + key2 = fan node index (interger) starting from 1 + value = path to fan device file (string) """ + _fan_to_device_path_mapping = {} + +#fan1_direction +#fan1_fault +#fan1_present + + #(FAN_NUM_2_IDX, FAN_NODE_DUTY_IDX_OF_MAP): 'fan2_duty_cycle_percentage', + _fan_to_device_node_mapping = { + (FAN_NUM_1_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan1_fault', + (FAN_NUM_1_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan1_direction', + + (FAN_NUM_2_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan2_fault', + (FAN_NUM_2_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan2_direction', + + (FAN_NUM_3_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan3_fault', + (FAN_NUM_3_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan3_direction', + + (FAN_NUM_4_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan4_fault', + (FAN_NUM_4_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan4_direction', + + (FAN_NUM_5_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan5_fault', + (FAN_NUM_5_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan5_direction', + + (FAN_NUM_6_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan6_fault', + (FAN_NUM_6_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan6_direction', + } + + def _get_fan_to_device_node(self, fan_num, node_num): + return self._fan_to_device_node_mapping[(fan_num, node_num)] + + def _get_fan_node_val(self, fan_num, node_num): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num:%d', fan_num) + return None + + if node_num < self.FAN_NODE_FAULT_IDX_OF_MAP or node_num > self.FAN_NODE_NUM_OF_MAP: + logging.debug('GET. Parameter error. node_num:%d', node_num) + return None + + device_path = self.get_fan_to_device_path(fan_num, node_num) + + try: + val_file = open(device_path, 'r') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + content = val_file.readline().rstrip() + + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return int(content) + + def _set_fan_node_val(self, fan_num, node_num, val): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num:%d', fan_num) + return None + + if node_num < self.FAN_NODE_FAULT_IDX_OF_MAP or node_num > self.FAN_NODE_NUM_OF_MAP: + logging.debug('GET. Parameter error. node_num:%d', node_num) + return None + + content = str(val) + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + device_path = self.get_fan_to_device_path(fan_num, node_num) + try: + val_file = open(device_path, 'w') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + val_file.write(content) + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return True + + def __init__(self): + fan_path = self.BASE_VAL_PATH + + for fan_num in range(self.FAN_NUM_1_IDX, self.FAN_NUM_ON_MAIN_BROAD+1): + for node_num in range(self.FAN_NODE_FAULT_IDX_OF_MAP, self.FAN_NODE_NUM_OF_MAP+1): + self._fan_to_device_path_mapping[(fan_num, node_num)] = fan_path.format( + self._fan_to_device_node_mapping[(fan_num, node_num)]) + + def get_num_fans(self): + return self.FAN_NUM_ON_MAIN_BROAD + + def get_idx_fan_start(self): + return self.FAN_NUM_1_IDX + + def get_num_nodes(self): + return self.FAN_NODE_NUM_OF_MAP + + def get_idx_node_start(self): + return self.FAN_NODE_FAULT_IDX_OF_MAP + + def get_size_node_map(self): + return len(self._fan_to_device_node_mapping) + + def get_size_path_map(self): + return len(self._fan_to_device_path_mapping) + + def get_fan_to_device_path(self, fan_num, node_num): + return self._fan_to_device_path_mapping[(fan_num, node_num)] + + def get_fan_fault(self, fan_num): + return self._get_fan_node_val(fan_num, self.FAN_NODE_FAULT_IDX_OF_MAP) + + #def get_fan_speed(self, fan_num): + # return self._get_fan_node_val(fan_num, self.FAN_NODE_SPEED_IDX_OF_MAP) + + def get_fan_dir(self, fan_num): + return self._get_fan_node_val(fan_num, self.FAN_NODE_DIR_IDX_OF_MAP) + + def get_fan_duty_cycle(self): + #duty_path = self.FAN_DUTY_PATH + try: + val_file = open(self.FAN_DUTY_PATH) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + content = val_file.readline().rstrip() + val_file.close() + + return int(content) + #self._get_fan_node_val(fan_num, self.FAN_NODE_DUTY_IDX_OF_MAP) +#static u32 reg_val_to_duty_cycle(u8 reg_val) +#{ +# reg_val &= FAN_DUTY_CYCLE_REG_MASK; +# return ((u32)(reg_val+1) * 625 + 75)/ 100; +#} +# + def set_fan_duty_cycle(self, val): + + try: + fan_file = open(self.FAN_DUTY_PATH, 'r+') + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + #val = ((val + 1 ) * 625 +75 ) / 100 + fan_file.write(str(val)) + fan_file.close() + return True + + #def get_fanr_fault(self, fan_num): + # return self._get_fan_node_val(fan_num, self.FANR_NODE_FAULT_IDX_OF_MAP) + + def get_fanr_speed(self, fan_num): + return self._get_fan_node_val(fan_num, self.FANR_NODE_SPEED_IDX_OF_MAP) + + def get_fan_status(self, fan_num): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num, %d', fan_num) + return None + + if self.get_fan_fault(fan_num) is not None and self.get_fan_fault(fan_num) > 0: + logging.debug('GET. FAN fault. fan_num, %d', fan_num) + return False + + #if self.get_fanr_fault(fan_num) is not None and self.get_fanr_fault(fan_num) > 0: + # logging.debug('GET. FANR fault. fan_num, %d', fan_num) + # return False + + return True + +#def main(): +# fan = FanUtil() +# +# print 'get_size_node_map : %d' % fan.get_size_node_map() +# print 'get_size_path_map : %d' % fan.get_size_path_map() +# for x in range(fan.get_idx_fan_start(), fan.get_num_fans()+1): +# for y in range(fan.get_idx_node_start(), fan.get_num_nodes()+1): +# print fan.get_fan_to_device_path(x, y) +# +#if __name__ == '__main__': +# main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/classes/thermalutil.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/classes/thermalutil.py new file mode 100755 index 000000000000..50d8c85d94ca --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/classes/thermalutil.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# 1/10/2018:Jostar modify for as7716_32x +# ------------------------------------------------------------------ + +try: + import time + import logging + import glob + from collections import namedtuple +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + + +class ThermalUtil(object): + """Platform-specific ThermalUtil class""" + + THERMAL_NUM_ON_MAIN_BROAD = 3 + THERMAL_NUM_1_IDX = 1 # 1_ON_MAIN_BROAD + THERMAL_NUM_2_IDX = 2 # 2_ON_MAIN_BROAD + THERMAL_NUM_3_IDX = 3 # 3_ON_MAIN_BROAD + + BASE_VAL_PATH = '/sys/bus/i2c/devices/{0}-00{1}/hwmon/hwmon*/temp1_input' + + """ Dictionary where + key1 = thermal id index (integer) starting from 1 + value = path to fan device file (string) """ + _thermal_to_device_path_mapping = {} + + _thermal_to_device_node_mapping = { + THERMAL_NUM_1_IDX: ['10', '48'], + THERMAL_NUM_2_IDX: ['10', '49'], + THERMAL_NUM_3_IDX: ['10', '4a'], + } + + def __init__(self): + thermal_path = self.BASE_VAL_PATH + + for x in range(self.THERMAL_NUM_1_IDX, self.THERMAL_NUM_ON_MAIN_BROAD+1): + self._thermal_to_device_path_mapping[x] = thermal_path.format( + self._thermal_to_device_node_mapping[x][0], + self._thermal_to_device_node_mapping[x][1]) + + def _get_thermal_node_val(self, thermal_num): + if thermal_num < self.THERMAL_NUM_1_IDX or thermal_num > self.THERMAL_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. thermal_num, %d', thermal_num) + return None + + device_path = self.get_thermal_to_device_path(thermal_num) + for filename in glob.glob(device_path): + try: + val_file = open(filename, 'r') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + content = val_file.readline().rstrip() + + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return int(content) + + + def get_num_thermals(self): + return self.THERMAL_NUM_ON_MAIN_BROAD + + def get_idx_thermal_start(self): + return self.THERMAL_NUM_1_IDX + + def get_size_node_map(self): + return len(self._thermal_to_device_node_mapping) + + def get_size_path_map(self): + return len(self._thermal_to_device_path_mapping) + + def get_thermal_to_device_path(self, thermal_num): + return self._thermal_to_device_path_mapping[thermal_num] + + def get_thermal_1_val(self): + return self._get_thermal_node_val(self.THERMAL_NUM_1_IDX) + + def get_thermal_2_val(self): + return self._get_thermal_node_val(self.THERMAL_NUM_2_IDX) + def get_thermal_temp(self): + return (self._get_thermal_node_val(self.THERMAL_NUM_1_IDX) + self._get_thermal_node_val(self.THERMAL_NUM_2_IDX) +self._get_thermal_node_val(self.THERMAL_NUM_3_IDX)) + +#def main(): +# thermal = ThermalUtil() +# +# print 'get_size_node_map : %d' % thermal.get_size_node_map() +# print 'get_size_path_map : %d' % thermal.get_size_path_map() +# for x in range(thermal.get_idx_thermal_start(), thermal.get_num_thermals()+1): +# print thermal.get_thermal_to_device_path(x) +# +#if __name__ == '__main__': +# main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/Makefile b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/Makefile new file mode 100755 index 000000000000..4c0942afb97e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/Makefile @@ -0,0 +1,21 @@ +ifneq ($(KERNELRELEASE),) +obj-m:= accton_as7716_32x_cpld1.o accton_as7716_32x_fan.o \ + accton_as7716_32x_leds.o accton_as7716_32x_psu.o cpr_4011_4mxx.o ym2651y.o \ + optoe.o accton_i2c_cpld.o + +else +ifeq (,$(KERNEL_SRC)) +#$(error KERNEL_SRC is not defined) +KVERSION := $(shell uname -r) +KERNEL_DIR = /usr/src/linux-headers-$(KVERSION)/ +KERNELDIR:=$(KERNEL_DIR) +#export KERNELDIR:=${PWD}/../../../../../src/sonic-linux-kernel/linux-3.16.43/ +else +KERNELDIR:=$(KERNEL_SRC) +endif +PWD:=$(shell pwd) +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules +clean: + rm -rf *.o *.mod.o *.mod.o *.ko .*cmd .tmp_versions Module.markers Module.symvers modules.order +endif diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_cpld1.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_cpld1.c new file mode 100755 index 000000000000..cbab013f5860 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_cpld1.c @@ -0,0 +1,781 @@ +/* + * A hwmon driver for the as7716_32x_cpld + * + * Copyright (C) 2013 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +#define I2C_RW_RETRY_COUNT 10 +#define I2C_RW_RETRY_INTERVAL 60 /* ms */ + +static ssize_t show_present(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_present_all(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t show_version(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t get_mode_reset(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t set_mode_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); + +static int as7716_32x_cpld_read_internal(struct i2c_client *client, u8 reg); +static int as7716_32x_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value); + +struct as7716_32x_cpld_data { + struct device *hwmon_dev; + struct mutex update_lock; +}; + +/* Addresses scanned for as7716_32x_cpld + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index +#define TRANSCEIVER_RESET_ATTR_ID(index) MODULE_RESET_##index + +enum as7716_32x_cpld_sysfs_attributes { + CPLD_VERSION, + ACCESS, + MODULE_PRESENT_ALL, + /* transceiver attributes */ + TRANSCEIVER_PRESENT_ATTR_ID(1), + TRANSCEIVER_PRESENT_ATTR_ID(2), + TRANSCEIVER_PRESENT_ATTR_ID(3), + TRANSCEIVER_PRESENT_ATTR_ID(4), + TRANSCEIVER_PRESENT_ATTR_ID(5), + TRANSCEIVER_PRESENT_ATTR_ID(6), + TRANSCEIVER_PRESENT_ATTR_ID(7), + TRANSCEIVER_PRESENT_ATTR_ID(8), + TRANSCEIVER_PRESENT_ATTR_ID(9), + TRANSCEIVER_PRESENT_ATTR_ID(10), + TRANSCEIVER_PRESENT_ATTR_ID(11), + TRANSCEIVER_PRESENT_ATTR_ID(12), + TRANSCEIVER_PRESENT_ATTR_ID(13), + TRANSCEIVER_PRESENT_ATTR_ID(14), + TRANSCEIVER_PRESENT_ATTR_ID(15), + TRANSCEIVER_PRESENT_ATTR_ID(16), + TRANSCEIVER_PRESENT_ATTR_ID(17), + TRANSCEIVER_PRESENT_ATTR_ID(18), + TRANSCEIVER_PRESENT_ATTR_ID(19), + TRANSCEIVER_PRESENT_ATTR_ID(20), + TRANSCEIVER_PRESENT_ATTR_ID(21), + TRANSCEIVER_PRESENT_ATTR_ID(22), + TRANSCEIVER_PRESENT_ATTR_ID(23), + TRANSCEIVER_PRESENT_ATTR_ID(24), + TRANSCEIVER_PRESENT_ATTR_ID(25), + TRANSCEIVER_PRESENT_ATTR_ID(26), + TRANSCEIVER_PRESENT_ATTR_ID(27), + TRANSCEIVER_PRESENT_ATTR_ID(28), + TRANSCEIVER_PRESENT_ATTR_ID(29), + TRANSCEIVER_PRESENT_ATTR_ID(30), + TRANSCEIVER_PRESENT_ATTR_ID(31), + TRANSCEIVER_PRESENT_ATTR_ID(32), + TRANSCEIVER_RESET_ATTR_ID(1), + TRANSCEIVER_RESET_ATTR_ID(2), + TRANSCEIVER_RESET_ATTR_ID(3), + TRANSCEIVER_RESET_ATTR_ID(4), + TRANSCEIVER_RESET_ATTR_ID(5), + TRANSCEIVER_RESET_ATTR_ID(6), + TRANSCEIVER_RESET_ATTR_ID(7), + TRANSCEIVER_RESET_ATTR_ID(8), + TRANSCEIVER_RESET_ATTR_ID(9), + TRANSCEIVER_RESET_ATTR_ID(10), + TRANSCEIVER_RESET_ATTR_ID(11), + TRANSCEIVER_RESET_ATTR_ID(12), + TRANSCEIVER_RESET_ATTR_ID(13), + TRANSCEIVER_RESET_ATTR_ID(14), + TRANSCEIVER_RESET_ATTR_ID(15), + TRANSCEIVER_RESET_ATTR_ID(16), + TRANSCEIVER_RESET_ATTR_ID(17), + TRANSCEIVER_RESET_ATTR_ID(18), + TRANSCEIVER_RESET_ATTR_ID(19), + TRANSCEIVER_RESET_ATTR_ID(20), + TRANSCEIVER_RESET_ATTR_ID(21), + TRANSCEIVER_RESET_ATTR_ID(22), + TRANSCEIVER_RESET_ATTR_ID(23), + TRANSCEIVER_RESET_ATTR_ID(24), + TRANSCEIVER_RESET_ATTR_ID(25), + TRANSCEIVER_RESET_ATTR_ID(26), + TRANSCEIVER_RESET_ATTR_ID(27), + TRANSCEIVER_RESET_ATTR_ID(28), + TRANSCEIVER_RESET_ATTR_ID(29), + TRANSCEIVER_RESET_ATTR_ID(30), + TRANSCEIVER_RESET_ATTR_ID(31), + TRANSCEIVER_RESET_ATTR_ID(32), +}; + +/* sysfs attributes for hwmon + */ + +/* transceiver attributes */ +/*present*/ +#define DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, show_present, NULL, MODULE_PRESENT_##index) +#define DECLARE_TRANSCEIVER_ATTR(index) &sensor_dev_attr_module_present_##index.dev_attr.attr + +/*reset*/ +#define DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_reset_##index, S_IWUSR | S_IRUGO, get_mode_reset, set_mode_reset, MODULE_RESET_##index) +#define DECLARE_TRANSCEIVER_RESET_ATTR(index) &sensor_dev_attr_module_reset_##index.dev_attr.attr + +static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_version, NULL, CPLD_VERSION); +static SENSOR_DEVICE_ATTR(access, S_IWUSR, NULL, access, ACCESS); +/* transceiver attributes */ +static SENSOR_DEVICE_ATTR(module_present_all, S_IRUGO, show_present_all, NULL, MODULE_PRESENT_ALL); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(1); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(2); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(3); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(4); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(5); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(6); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(7); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(8); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(9); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(10); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(11); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(12); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(13); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(14); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(15); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(16); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(17); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(18); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(19); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(20); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(21); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(22); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(23); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(24); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(25); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(26); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(27); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(28); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(29); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(30); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(31); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(32); + +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(1); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(2); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(3); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(4); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(5); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(6); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(7); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(8); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(9); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(10); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(11); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(12); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(13); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(14); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(15); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(16); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(17); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(18); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(19); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(20); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(21); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(22); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(23); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(24); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(25); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(26); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(27); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(28); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(29); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(30); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(31); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(32); + + +static struct attribute *as7716_32x_cpld_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + /* transceiver attributes */ + &sensor_dev_attr_module_present_all.dev_attr.attr, + DECLARE_TRANSCEIVER_ATTR(1), + DECLARE_TRANSCEIVER_ATTR(2), + DECLARE_TRANSCEIVER_ATTR(3), + DECLARE_TRANSCEIVER_ATTR(4), + DECLARE_TRANSCEIVER_ATTR(5), + DECLARE_TRANSCEIVER_ATTR(6), + DECLARE_TRANSCEIVER_ATTR(7), + DECLARE_TRANSCEIVER_ATTR(8), + DECLARE_TRANSCEIVER_ATTR(9), + DECLARE_TRANSCEIVER_ATTR(10), + DECLARE_TRANSCEIVER_ATTR(11), + DECLARE_TRANSCEIVER_ATTR(12), + DECLARE_TRANSCEIVER_ATTR(13), + DECLARE_TRANSCEIVER_ATTR(14), + DECLARE_TRANSCEIVER_ATTR(15), + DECLARE_TRANSCEIVER_ATTR(16), + DECLARE_TRANSCEIVER_ATTR(17), + DECLARE_TRANSCEIVER_ATTR(18), + DECLARE_TRANSCEIVER_ATTR(19), + DECLARE_TRANSCEIVER_ATTR(20), + DECLARE_TRANSCEIVER_ATTR(21), + DECLARE_TRANSCEIVER_ATTR(22), + DECLARE_TRANSCEIVER_ATTR(23), + DECLARE_TRANSCEIVER_ATTR(24), + DECLARE_TRANSCEIVER_ATTR(25), + DECLARE_TRANSCEIVER_ATTR(26), + DECLARE_TRANSCEIVER_ATTR(27), + DECLARE_TRANSCEIVER_ATTR(28), + DECLARE_TRANSCEIVER_ATTR(29), + DECLARE_TRANSCEIVER_ATTR(30), + DECLARE_TRANSCEIVER_ATTR(31), + DECLARE_TRANSCEIVER_ATTR(32), + DECLARE_TRANSCEIVER_RESET_ATTR(1), + DECLARE_TRANSCEIVER_RESET_ATTR(2), + DECLARE_TRANSCEIVER_RESET_ATTR(3), + DECLARE_TRANSCEIVER_RESET_ATTR(4), + DECLARE_TRANSCEIVER_RESET_ATTR(5), + DECLARE_TRANSCEIVER_RESET_ATTR(6), + DECLARE_TRANSCEIVER_RESET_ATTR(7), + DECLARE_TRANSCEIVER_RESET_ATTR(8), + DECLARE_TRANSCEIVER_RESET_ATTR(9), + DECLARE_TRANSCEIVER_RESET_ATTR(10), + DECLARE_TRANSCEIVER_RESET_ATTR(11), + DECLARE_TRANSCEIVER_RESET_ATTR(12), + DECLARE_TRANSCEIVER_RESET_ATTR(13), + DECLARE_TRANSCEIVER_RESET_ATTR(14), + DECLARE_TRANSCEIVER_RESET_ATTR(15), + DECLARE_TRANSCEIVER_RESET_ATTR(16), + DECLARE_TRANSCEIVER_RESET_ATTR(17), + DECLARE_TRANSCEIVER_RESET_ATTR(18), + DECLARE_TRANSCEIVER_RESET_ATTR(19), + DECLARE_TRANSCEIVER_RESET_ATTR(20), + DECLARE_TRANSCEIVER_RESET_ATTR(21), + DECLARE_TRANSCEIVER_RESET_ATTR(22), + DECLARE_TRANSCEIVER_RESET_ATTR(23), + DECLARE_TRANSCEIVER_RESET_ATTR(24), + DECLARE_TRANSCEIVER_RESET_ATTR(25), + DECLARE_TRANSCEIVER_RESET_ATTR(26), + DECLARE_TRANSCEIVER_RESET_ATTR(27), + DECLARE_TRANSCEIVER_RESET_ATTR(28), + DECLARE_TRANSCEIVER_RESET_ATTR(29), + DECLARE_TRANSCEIVER_RESET_ATTR(30), + DECLARE_TRANSCEIVER_RESET_ATTR(31), + DECLARE_TRANSCEIVER_RESET_ATTR(32), + NULL +}; + +static const struct attribute_group as7716_32x_cpld_group = { + .attrs = as7716_32x_cpld_attributes, +}; + +static ssize_t show_present_all(struct device *dev, struct device_attribute *da, + char *buf) +{ + int i, status; + u8 values[4] = {0}; + u8 regs[] = {0x30, 0x31, 0x32, 0x33}; + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_cpld_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + status = as7716_32x_cpld_read_internal(client, regs[i]); + + if (status < 0) { + goto exit; + } + + values[i] = ~(u8)status; + } + + mutex_unlock(&data->update_lock); + + /* Return values 1 -> 32 in order */ + return sprintf(buf, "%.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], + values[3]); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_present(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_cpld_data *data = i2c_get_clientdata(client); + int status = 0; + u8 reg = 0, mask = 0; + + switch (attr->index) { + case MODULE_PRESENT_1 ... MODULE_PRESENT_8: + reg = 0x30; + mask = 0x1 << (attr->index - MODULE_PRESENT_1); + break; + case MODULE_PRESENT_9 ... MODULE_PRESENT_16: + reg = 0x31; + mask = 0x1 << (attr->index - MODULE_PRESENT_9); + break; + case MODULE_PRESENT_17 ... MODULE_PRESENT_24: + reg = 0x32; + mask = 0x1 << (attr->index - MODULE_PRESENT_17); + break; + case MODULE_PRESENT_25 ... MODULE_PRESENT_32: + reg = 0x33; + mask = 0x1 << (attr->index - MODULE_PRESENT_25); + break; + default: + return 0; + } + + + mutex_lock(&data->update_lock); + status = as7716_32x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", !(status & mask)); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_version(struct device *dev, struct device_attribute *da, + char *buf) +{ + u8 reg = 0, mask = 0; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_cpld_data *data = i2c_get_clientdata(client); + int status = 0; + + switch (attr->index) { + case CPLD_VERSION: + reg = 0x1; + mask = 0xFF; + break; + default: + break; + } + + mutex_lock(&data->update_lock); + status = as7716_32x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d\n", (status & mask)); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int status; + u32 addr, val; + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_cpld_data *data = i2c_get_clientdata(client); + + if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) { + return -EINVAL; + } + + if (addr > 0xFF || val > 0xFF) { + return -EINVAL; + } + + mutex_lock(&data->update_lock); + status = as7716_32x_cpld_write_internal(client, addr, val); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static int as7716_32x_cpld_read_internal(struct i2c_client *client, u8 reg) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, reg); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static int as7716_32x_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_write_byte_data(client, reg, value); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static void as7716_32x_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void as7716_32x_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + + mutex_unlock(&list_lock); +} + +static int as7716_32x_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + struct as7716_32x_cpld_data *data = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_dbg(&client->dev, "i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7716_32x_cpld_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7716_32x_cpld_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + as7716_32x_cpld_add_client(client); + + dev_info(&client->dev, "%s: cpld '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7716_32x_cpld_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7716_32x_cpld_remove(struct i2c_client *client) +{ + struct as7716_32x_cpld_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7716_32x_cpld_group); + kfree(data); + as7716_32x_cpld_remove_client(client); + + return 0; +} + +int as7716_32x_cpld_read(unsigned short cpld_addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = i2c_smbus_read_byte_data(cpld_node->client, reg); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as7716_32x_cpld_read); + +int as7716_32x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = i2c_smbus_write_byte_data(cpld_node->client, reg, value); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as7716_32x_cpld_write); + +static ssize_t get_mode_reset(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_cpld_data *data = i2c_get_clientdata(client); + int status = 0; + u8 reg = 0, mask = 0; + + switch (attr->index) { + case MODULE_RESET_1 ... MODULE_RESET_8: + reg = 0x04; + mask = 0x1 << (attr->index - MODULE_RESET_1); + break; + case MODULE_RESET_9 ... MODULE_RESET_16: + reg = 0x05; + mask = 0x1 << (attr->index - MODULE_RESET_9); + break; + case MODULE_RESET_17 ... MODULE_RESET_24: + reg = 0x06; + mask = 0x1 << (attr->index - MODULE_RESET_17); + break; + case MODULE_RESET_25 ... MODULE_RESET_32: + reg = 0x07; + mask = 0x1 << (attr->index - MODULE_RESET_25); + break; + default: + return 0; + } + + + mutex_lock(&data->update_lock); + status = as7716_32x_cpld_read_internal(client, reg); + + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\r\n", !(status & mask)); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t set_mode_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_cpld_data *data = i2c_get_clientdata(client); + long reset; + int status=0, val, error; + u8 reg = 0, mask = 0; + + + error = kstrtol(buf, 10, &reset); + if (error) { + return error; + } + + switch (attr->index) { + case MODULE_RESET_1 ... MODULE_RESET_8: + reg = 0x04; + mask = 0x1 << (attr->index - MODULE_RESET_1); + break; + case MODULE_RESET_9 ... MODULE_RESET_16: + reg = 0x05; + mask = 0x1 << (attr->index - MODULE_RESET_9); + break; + case MODULE_RESET_17 ... MODULE_RESET_24: + reg = 0x06; + mask = 0x1 << (attr->index - MODULE_RESET_17); + break; + case MODULE_RESET_25 ... MODULE_RESET_32: + reg = 0x07; + mask = 0x1 << (attr->index - MODULE_RESET_25); + break; + default: + return 0; + } + mutex_lock(&data->update_lock); + + status = as7716_32x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + + /* Update lp_mode status */ + if (reset) + { + val = status&(~mask); + } + else + { + val =status | (mask); + } + + status = as7716_32x_cpld_write_internal(client, reg, val); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + + + +static const struct i2c_device_id as7716_32x_cpld_id[] = { + { "as7716_32x_cpld1", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7716_32x_cpld_id); + +static struct i2c_driver as7716_32x_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7716_32x_cpld1", + }, + .probe = as7716_32x_cpld_probe, + .remove = as7716_32x_cpld_remove, + .id_table = as7716_32x_cpld_id, + .address_list = normal_i2c, +}; + +static int __init as7716_32x_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&as7716_32x_cpld_driver); +} + +static void __exit as7716_32x_cpld_exit(void) +{ + i2c_del_driver(&as7716_32x_cpld_driver); +} + +module_init(as7716_32x_cpld_init); +module_exit(as7716_32x_cpld_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7716_32x_cpld driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_fan.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_fan.c new file mode 100755 index 000000000000..83b7bceeb99e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_fan.c @@ -0,0 +1,794 @@ +/* + * A hwmon driver for the Accton as7716 32x fan + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as7716_32x_fan" + +#define NUM_THERMAL_SENSORS (3) /* Get sum of this number of sensors.*/ +#define THERMAL_SENSORS_DRIVER "lm75" + +#define IN +#define OUT + +static struct as7716_32x_fan_data *as7716_32x_fan_update_device(struct device *dev); +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t get_enable(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t get_sys_temp(struct device *dev, struct device_attribute *da, char *buf); +extern int as7716_32x_cpld_read(unsigned short cpld_addr, u8 reg); +extern int as7716_32x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +/* fan related data, the index should match sysfs_fan_attributes + */ +static const u8 fan_reg[] = { + 0x0F, /* fan 1-6 present status */ + 0x10, /* fan 1-6 direction(0:B2F 1:F2B) */ + 0x11, /* fan PWM(for all fan) */ + 0x12, /* front fan 1 speed(rpm) */ + 0x13, /* front fan 2 speed(rpm) */ + 0x14, /* front fan 3 speed(rpm) */ + 0x15, /* front fan 4 speed(rpm) */ + 0x16, /* front fan 5 speed(rpm) */ + 0x17, /* front fan 6 speed(rpm) */ + 0x22, /* rear fan 1 speed(rpm) */ + 0x23, /* rear fan 2 speed(rpm) */ + 0x24, /* rear fan 3 speed(rpm) */ + 0x25, /* rear fan 4 speed(rpm) */ + 0x26, /* rear fan 5 speed(rpm) */ + 0x27, /* rear fan 6 speed(rpm) */ +}; + +/* Each client has this additional data */ +struct as7716_32x_fan_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[ARRAY_SIZE(fan_reg)]; /* Register value */ + u8 enable; + int system_temp; /*In unit of mini-Celsius*/ + int sensors_found; +}; + +enum fan_id { + FAN1_ID, + FAN2_ID, + FAN3_ID, + FAN4_ID, + FAN5_ID, + FAN6_ID +}; + +enum sysfs_fan_attributes { + FAN_PRESENT_REG, + FAN_DIRECTION_REG, + FAN_DUTY_CYCLE_PERCENTAGE, /* Only one CPLD register to control duty cycle for all fans */ + FAN1_FRONT_SPEED_RPM, + FAN2_FRONT_SPEED_RPM, + FAN3_FRONT_SPEED_RPM, + FAN4_FRONT_SPEED_RPM, + FAN5_FRONT_SPEED_RPM, + FAN6_FRONT_SPEED_RPM, + FAN1_REAR_SPEED_RPM, + FAN2_REAR_SPEED_RPM, + FAN3_REAR_SPEED_RPM, + FAN4_REAR_SPEED_RPM, + FAN5_REAR_SPEED_RPM, + FAN6_REAR_SPEED_RPM, + FAN1_DIRECTION, + FAN2_DIRECTION, + FAN3_DIRECTION, + FAN4_DIRECTION, + FAN5_DIRECTION, + FAN6_DIRECTION, + FAN1_PRESENT, + FAN2_PRESENT, + FAN3_PRESENT, + FAN4_PRESENT, + FAN5_PRESENT, + FAN6_PRESENT, + FAN1_FAULT, + FAN2_FAULT, + FAN3_FAULT, + FAN4_FAULT, + FAN5_FAULT, + FAN6_FAULT +}; + +/* Define attributes + */ +#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT);\ + static SENSOR_DEVICE_ATTR(fan##index2##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT) +#define DECLARE_FAN_FAULT_ATTR(index, index2) &sensor_dev_attr_fan##index##_fault.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_fault.dev_attr.attr + +#define DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_direction, S_IRUGO, fan_show_value, NULL, FAN##index##_DIRECTION) +#define DECLARE_FAN_DIRECTION_ATTR(index) &sensor_dev_attr_fan##index##_direction.dev_attr.attr + +#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN_DUTY_CYCLE_PERCENTAGE);\ + static SENSOR_DEVICE_ATTR(pwm##index, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN_DUTY_CYCLE_PERCENTAGE);\ + static SENSOR_DEVICE_ATTR(pwm##index##_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, FAN_DUTY_CYCLE_PERCENTAGE) + +#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) &sensor_dev_attr_fan_duty_cycle_percentage.dev_attr.attr, \ + &sensor_dev_attr_pwm##index.dev_attr.attr, \ + &sensor_dev_attr_pwm##index##_enable.dev_attr.attr + +#define DECLARE_FAN_SYSTEM_TEMP_SENSOR_DEV_ATTR() \ + static SENSOR_DEVICE_ATTR(sys_temp, S_IRUGO, get_sys_temp, NULL, FAN_DUTY_CYCLE_PERCENTAGE) + +#define DECLARE_FAN_SYSTEM_TEMP_ATTR() &sensor_dev_attr_sys_temp.dev_attr.attr + + +#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, fan_show_value, NULL, FAN##index##_PRESENT) +#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr + +#define DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_front_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_rear_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_input, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index2##_input, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM) +#define DECLARE_FAN_SPEED_RPM_ATTR(index, index2) &sensor_dev_attr_fan##index##_front_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_rear_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_input.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_input.dev_attr.attr + +/* 6 fan fault attributes in this platform */ +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1,11); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2,12); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(3,13); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(4,14); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(5,15); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(6,16); +/* 6 fan speed(rpm) attributes in this platform */ +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1,11); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2,12); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(3,13); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(4,14); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(5,15); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(6,16); +/* 6 fan present attributes in this platform */ +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(5); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(6); + +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(1); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(2); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(3); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(4); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(5); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(6); + +/* 1 fan duty cycle attribute in this platform */ +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(1); +/* System temperature for fancontrol */ +DECLARE_FAN_SYSTEM_TEMP_SENSOR_DEV_ATTR(); + +static struct attribute *as7716_32x_fan_attributes[] = { + /* fan related attributes */ + DECLARE_FAN_FAULT_ATTR(1,11), + DECLARE_FAN_FAULT_ATTR(2,12), + DECLARE_FAN_FAULT_ATTR(3,13), + DECLARE_FAN_FAULT_ATTR(4,14), + DECLARE_FAN_FAULT_ATTR(5,15), + DECLARE_FAN_FAULT_ATTR(6,16), + DECLARE_FAN_SPEED_RPM_ATTR(1,11), + DECLARE_FAN_SPEED_RPM_ATTR(2,12), + DECLARE_FAN_SPEED_RPM_ATTR(3,13), + DECLARE_FAN_SPEED_RPM_ATTR(4,14), + DECLARE_FAN_SPEED_RPM_ATTR(5,15), + DECLARE_FAN_SPEED_RPM_ATTR(6,16), + DECLARE_FAN_PRESENT_ATTR(1), + DECLARE_FAN_PRESENT_ATTR(2), + DECLARE_FAN_PRESENT_ATTR(3), + DECLARE_FAN_PRESENT_ATTR(4), + DECLARE_FAN_PRESENT_ATTR(5), + DECLARE_FAN_PRESENT_ATTR(6), + DECLARE_FAN_DIRECTION_ATTR(1), + DECLARE_FAN_DIRECTION_ATTR(2), + DECLARE_FAN_DIRECTION_ATTR(3), + DECLARE_FAN_DIRECTION_ATTR(4), + DECLARE_FAN_DIRECTION_ATTR(5), + DECLARE_FAN_DIRECTION_ATTR(6), + DECLARE_FAN_DUTY_CYCLE_ATTR(1), + DECLARE_FAN_SYSTEM_TEMP_ATTR(), + NULL +}; + +#define FAN_DUTY_CYCLE_REG_MASK 0xF +#define FAN_MAX_DUTY_CYCLE 100 +#define FAN_REG_VAL_TO_SPEED_RPM_STEP 100 + +static int as7716_32x_fan_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int as7716_32x_fan_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* fan utility functions + */ +static u32 reg_val_to_duty_cycle(u8 reg_val) +{ + reg_val &= FAN_DUTY_CYCLE_REG_MASK; + return ((u32)(reg_val+1) * 625 + 75)/ 100; +} + +static u8 duty_cycle_to_reg_val(u8 duty_cycle) +{ + return ((u32)duty_cycle * 100 / 625) - 1; +} + +static u32 reg_val_to_speed_rpm(u8 reg_val) +{ + return (u32)reg_val * FAN_REG_VAL_TO_SPEED_RPM_STEP; +} + +static u8 reg_val_to_direction(u8 reg_val, enum fan_id id) +{ + u8 mask = (1 << id); + + reg_val &= mask; + + return reg_val ? 1 : 0; +} +static u8 reg_val_to_is_present(u8 reg_val, enum fan_id id) +{ + u8 mask = (1 << id); + + reg_val &= mask; + + return reg_val ? 0 : 1; +} + +static u8 is_fan_fault(struct as7716_32x_fan_data *data, enum fan_id id) +{ + u8 ret = 1; + int front_fan_index = FAN1_FRONT_SPEED_RPM + id; + int rear_fan_index = FAN1_REAR_SPEED_RPM + id; + + /* Check if the speed of front or rear fan is ZERO, + */ + if (reg_val_to_speed_rpm(data->reg_val[front_fan_index]) && + reg_val_to_speed_rpm(data->reg_val[rear_fan_index])) { + ret = 0; + } + + return ret; +} + +static ssize_t set_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct as7716_32x_fan_data *data = as7716_32x_fan_update_device(dev); + int error, value; + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < 0 || value > 1) + return -EINVAL; + + data->enable = value; + if (value == 0) + { + return set_duty_cycle(dev, da, buf, FAN_MAX_DUTY_CYCLE); + } + return count; +} + + +static ssize_t get_enable(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as7716_32x_fan_data *data = as7716_32x_fan_update_device(dev); + + return sprintf(buf, "%u\n", data->enable); +} +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int error, value; + struct i2c_client *client = to_i2c_client(dev); + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < 0 || value > FAN_MAX_DUTY_CYCLE) + return -EINVAL; + + as7716_32x_fan_write_value(client, 0x33, 0); /* Disable fan speed watch dog */ + as7716_32x_fan_write_value(client, fan_reg[FAN_DUTY_CYCLE_PERCENTAGE], duty_cycle_to_reg_val(value)); + return count; +} + +/* Due to this struct is declared at lm75.c, it cannot be include + * under Sonic environment. I duplicate it from lm75.c. + */ +struct lm75_data { + struct i2c_client *client; + struct device *hwmon_dev; + struct thermal_zone_device *tz; + struct mutex update_lock; + u8 orig_conf; + u8 resolution; /* In bits, between 9 and 12 */ + u8 resolution_limits; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long sample_time; /* In jiffies */ + s16 temp[3]; /* Register values, + 0 = input + 1 = max + 2 = hyst */ +}; + +/*Copied from lm75.c*/ +static inline long lm75_reg_to_mc(s16 temp, u8 resolution) +{ + return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); +} + +/*Get hwmon_dev from i2c_client, set hwmon_dev = NULL is failed.*/ +static struct device * get_hwmon_dev( + struct i2c_client *client) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + if(data) + { + if( data->valid == 1 && data->hwmon_dev) + { + return data->hwmon_dev; + } + + } + return NULL; +} + +/* To find hwmon index by opening hwmon under that i2c address. + */ +static int find_hwmon_index_by_FileOpen( + int bus_nr, + unsigned short addr, + OUT int *index) +{ +#define MAX_HWMON_DEVICE (10) /* Find hwmon device in 0~10*/ + struct file *sfd; + char client_name[96]; + int i=0; + + do { + snprintf(client_name, sizeof(client_name), + "/sys/bus/i2c/devices/%d-%04x/hwmon/hwmon%d/temp1_input", + bus_nr, addr, i); + + sfd = filp_open(client_name, O_RDONLY, 0); + i++; + } while( IS_ERR(sfd) && i < MAX_HWMON_DEVICE); + + if (IS_ERR(sfd)) { + pr_err("Failed to open file(%s)#%d\r\n", client_name, __LINE__); + return -ENOENT; + } + filp_close(sfd, 0); + *index = i - 1; + return 0; + +#undef MAX_HWMON_DEVICE +} + +static int get_temp_file_path( + int bus_nr, unsigned short addr, + struct device *hwmon_dev + ,char *path, int max_len) +{ + + if(hwmon_dev && strlen(dev_name(hwmon_dev))) + { + snprintf(path, max_len, + "/sys/bus/i2c/devices/%d-%04x/hwmon/%s/temp1_input", + bus_nr, addr, dev_name(hwmon_dev)); + } + else + { + int i=0; + if(find_hwmon_index_by_FileOpen( bus_nr, addr, &i)) + { + return -EIO; + } + snprintf(path, max_len, + "/sys/bus/i2c/devices/%d-%04x/hwmon/hwmon%d/temp1_input", + bus_nr, addr, i); + } + return 0; +} + +/*File read the dev file at user space.*/ +static int read_devfile_temp1_input( + struct device *dev, + int bus_nr, + unsigned short addr, + struct device *hwmon_dev, + int *miniCelsius) +{ + struct file *sfd; + char buffer[96]; + char devfile[96]; + int rc, status; + int rdlen, value; + mm_segment_t old_fs; + + rc = 0; + get_temp_file_path(bus_nr, addr, hwmon_dev, devfile, sizeof(devfile)); + sfd = filp_open(devfile, O_RDONLY, 0); + if (IS_ERR(sfd)) { + pr_err("Failed to open file(%s)#%d\r\n", devfile, __LINE__); + return -ENOENT; + } + dev_dbg(dev, "Found device:%s\n",devfile); + + if(!(sfd->f_op) || !(sfd->f_op->read) ) { + pr_err("file %s cann't readable ?\n",devfile); + return -ENOENT; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + rdlen = sfd->f_op->read(sfd, buffer, sizeof(buffer), &sfd->f_pos); + if (rdlen == 0) { + pr_err( "File(%s) empty!\n", devfile); + rc = -EIO; + goto exit; + } + status = sscanf(buffer, "%d", &value); + if (status != 1) { + rc = -EIO; + goto exit; + } + *miniCelsius = value; + dev_dbg(dev,"found sensors: %d @i2c %d-%04x\n", value, bus_nr, addr); + +exit: + set_fs(old_fs); + filp_close(sfd, 0); + return rc; +} + +static u8 is_lm75_data_due(struct i2c_client *client) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + if (time_after(jiffies, data->last_updated + data->sample_time)) + { + return 1; + } + return 0; +} +static int get_lm75_temp(struct i2c_client *client, int *miniCelsius) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + *miniCelsius = lm75_reg_to_mc(data->temp[0], data->resolution); + + return 0; +} + +static int _find_lm75_device(struct device *dev, void *data) +{ + struct device_driver *driver; + struct as7716_32x_fan_data *prv = data; + char *driver_name = THERMAL_SENSORS_DRIVER; + + driver = dev->driver; + if (driver && driver->name && + strcmp(driver->name, driver_name) == 0) + { + struct i2c_client *client; + client = to_i2c_client(dev); + if (client) + { + /*cannot use "struct i2c_adapter *adap = to_i2c_adapter(dev);"*/ + struct i2c_adapter *adap = client->adapter; + int miniCelsius = 0; + + + if (!adap) { + return -ENXIO; + } + + /* If the data is not updated, read them from devfile + to drive them updateing data from chip.*/ + if (is_lm75_data_due(client)) + { + struct device *hwmon_dev; + + hwmon_dev = get_hwmon_dev(client); + if(0 == read_devfile_temp1_input(dev, adap->nr, + client->addr, hwmon_dev, &miniCelsius)) + { + prv->system_temp += miniCelsius; + prv->sensors_found++; + } + + } + else + { + get_lm75_temp(client, &miniCelsius); + prv->system_temp += miniCelsius; + prv->sensors_found++; + + } + } + } + return 0; +} + +/*Find all lm75 devices and return sum of temperatures.*/ +static ssize_t get_sys_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + ssize_t ret = 0; + struct as7716_32x_fan_data *data = as7716_32x_fan_update_device(dev); + + data->system_temp=0; + data->sensors_found=0; + i2c_for_each_dev(data, _find_lm75_device); + if (NUM_THERMAL_SENSORS != data->sensors_found) + { + dev_dbg(dev,"only %d of %d temps are found\n", + data->sensors_found, NUM_THERMAL_SENSORS); + data->system_temp = 0; + } + ret = sprintf(buf, "%d\n",data->system_temp); + return ret; +} +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7716_32x_fan_data *data = as7716_32x_fan_update_device(dev); + ssize_t ret = 0; + + if (data->valid) { + switch (attr->index) { + case FAN_DUTY_CYCLE_PERCENTAGE: + { + u32 duty_cycle = reg_val_to_duty_cycle(data->reg_val[FAN_DUTY_CYCLE_PERCENTAGE]); + ret = sprintf(buf, "%u\n", duty_cycle); + break; + } + case FAN1_FRONT_SPEED_RPM: + case FAN2_FRONT_SPEED_RPM: + case FAN3_FRONT_SPEED_RPM: + case FAN4_FRONT_SPEED_RPM: + case FAN5_FRONT_SPEED_RPM: + case FAN6_FRONT_SPEED_RPM: + case FAN1_REAR_SPEED_RPM: + case FAN2_REAR_SPEED_RPM: + case FAN3_REAR_SPEED_RPM: + case FAN4_REAR_SPEED_RPM: + case FAN5_REAR_SPEED_RPM: + case FAN6_REAR_SPEED_RPM: + ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index])); + break; + case FAN1_PRESENT: + case FAN2_PRESENT: + case FAN3_PRESENT: + case FAN4_PRESENT: + case FAN5_PRESENT: + case FAN6_PRESENT: + ret = sprintf(buf, "%d\n", + reg_val_to_is_present(data->reg_val[FAN_PRESENT_REG], + attr->index - FAN1_PRESENT)); + break; + case FAN1_FAULT: + case FAN2_FAULT: + case FAN3_FAULT: + case FAN4_FAULT: + case FAN5_FAULT: + case FAN6_FAULT: + ret = sprintf(buf, "%d\n", is_fan_fault(data, attr->index - FAN1_FAULT)); + break; + case FAN1_DIRECTION: + case FAN2_DIRECTION: + case FAN3_DIRECTION: + case FAN4_DIRECTION: + case FAN5_DIRECTION: + case FAN6_DIRECTION: + ret = sprintf(buf, "%d\n", + reg_val_to_direction(data->reg_val[FAN_DIRECTION_REG], + attr->index - FAN1_DIRECTION)); + break; + default: + break; + } + } + + return ret; +} + +static const struct attribute_group as7716_32x_fan_group = { + .attrs = as7716_32x_fan_attributes, +}; + +static struct as7716_32x_fan_data *as7716_32x_fan_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_fan_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || + !data->valid) { + int i; + + dev_dbg(&client->dev, "Starting as7716_32x_fan update\n"); + data->valid = 0; + + /* Update fan data + */ + for (i = 0; i < ARRAY_SIZE(data->reg_val); i++) { + int status = as7716_32x_fan_read_value(client, fan_reg[i]); + + if (status < 0) { + data->valid = 0; + mutex_unlock(&data->update_lock); + dev_dbg(&client->dev, "reg %d, err %d\n", fan_reg[i], status); + return data; + } + else { + data->reg_val[i] = status; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int as7716_32x_fan_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7716_32x_fan_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7716_32x_fan_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, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7716_32x_fan_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: fan '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7716_32x_fan_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7716_32x_fan_remove(struct i2c_client *client) +{ + struct as7716_32x_fan_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7716_32x_fan_group); + + return 0; +} + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x66, I2C_CLIENT_END }; + +static const struct i2c_device_id as7716_32x_fan_id[] = { + { "as7716_32x_fan", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7716_32x_fan_id); + +static struct i2c_driver as7716_32x_fan_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .probe = as7716_32x_fan_probe, + .remove = as7716_32x_fan_remove, + .id_table = as7716_32x_fan_id, + .address_list = normal_i2c, +}; + +static int __init as7716_32x_fan_init(void) +{ + return i2c_add_driver(&as7716_32x_fan_driver); +} + +static void __exit as7716_32x_fan_exit(void) +{ + i2c_del_driver(&as7716_32x_fan_driver); +} + +module_init(as7716_32x_fan_init); +module_exit(as7716_32x_fan_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7716_32x_fan driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_leds.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_leds.c new file mode 100755 index 000000000000..da3d9442035f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_leds.c @@ -0,0 +1,442 @@ +/* + * A LED driver for the as7716_32x_led + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * 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. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern int as7716_32x_cpld_read (unsigned short cpld_addr, u8 reg); +extern int as7716_32x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +extern void led_classdev_unregister(struct led_classdev *led_cdev); +extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev); +extern void led_classdev_resume(struct led_classdev *led_cdev); +extern void led_classdev_suspend(struct led_classdev *led_cdev); + +#define DRVNAME "as7716_32x_led" + +struct as7716_32x_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[1]; /* only 1 register*/ +}; + +static struct as7716_32x_led_data *ledctl = NULL; + +/* LED related data + */ + +#define LED_CNTRLER_I2C_ADDRESS (0x60) + +#define LED_TYPE_DIAG_REG_MASK (0x3) +#define LED_MODE_DIAG_GREEN_VALUE (0x02) +#define LED_MODE_DIAG_RED_VALUE (0x01) +#define LED_MODE_DIAG_AMBER_VALUE (0x00) /*It's yellow actually. Green+Red=Yellow*/ +#define LED_MODE_DIAG_OFF_VALUE (0x03) + + +#define LED_TYPE_LOC_REG_MASK (0x80) +#define LED_MODE_LOC_ON_VALUE (0) +#define LED_MODE_LOC_OFF_VALUE (0x80) + +enum led_type { + LED_TYPE_DIAG, + LED_TYPE_LOC, + LED_TYPE_FAN, + LED_TYPE_PSU1, + LED_TYPE_PSU2 +}; + +struct led_reg { + u32 types; + u8 reg_addr; +}; + +static const struct led_reg led_reg_map[] = { + {(1<update_lock); + + if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) + || !ledctl->valid) { + int i; + + dev_dbg(&ledctl->pdev->dev, "Starting as7716_32x_led update\n"); + + /* Update LED data + */ + for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { + int status = as7716_32x_led_read_value(led_reg_map[i].reg_addr); + + if (status < 0) { + ledctl->valid = 0; + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg_map[i].reg_addr, status); + goto exit; + } + else + { + ledctl->reg_val[i] = status; + } + } + + ledctl->last_updated = jiffies; + ledctl->valid = 1; + } + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void as7716_32x_led_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode, + enum led_type type) +{ + int reg_val; + u8 reg ; + mutex_lock(&ledctl->update_lock); + + if( !getLedReg(type, ®)) + { + dev_dbg(&ledctl->pdev->dev, "Not match item for %d.\n", type); + } + + reg_val = as7716_32x_led_read_value(reg); + + if (reg_val < 0) { + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", reg, reg_val); + goto exit; + } + reg_val = led_light_mode_to_reg_val(type, led_light_mode, reg_val); + as7716_32x_led_write_value(reg, reg_val); + + /* to prevent the slow-update issue */ + ledctl->valid = 0; + +exit: + mutex_unlock(&ledctl->update_lock); +} + + +static void as7716_32x_led_diag_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + as7716_32x_led_set(led_cdev, led_light_mode, LED_TYPE_DIAG); +} + +static enum led_brightness as7716_32x_led_diag_get(struct led_classdev *cdev) +{ + as7716_32x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); +} + +static void as7716_32x_led_loc_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + as7716_32x_led_set(led_cdev, led_light_mode, LED_TYPE_LOC); +} + +static enum led_brightness as7716_32x_led_loc_get(struct led_classdev *cdev) +{ + as7716_32x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); +} + +static void as7716_32x_led_auto_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ +} + +static enum led_brightness as7716_32x_led_auto_get(struct led_classdev *cdev) +{ + return LED_MODE_AUTO; +} + +static struct led_classdev as7716_32x_leds[] = { + [LED_TYPE_DIAG] = { + .name = "as7716_32x_led::diag", + .default_trigger = "unused", + .brightness_set = as7716_32x_led_diag_set, + .brightness_get = as7716_32x_led_diag_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_RED, + }, + [LED_TYPE_LOC] = { + .name = "as7716_32x_led::loc", + .default_trigger = "unused", + .brightness_set = as7716_32x_led_loc_set, + .brightness_get = as7716_32x_led_loc_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_BLUE, + }, + [LED_TYPE_FAN] = { + .name = "as7716_32x_led::fan", + .default_trigger = "unused", + .brightness_set = as7716_32x_led_auto_set, + .brightness_get = as7716_32x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU1] = { + .name = "as7716_32x_led::psu1", + .default_trigger = "unused", + .brightness_set = as7716_32x_led_auto_set, + .brightness_get = as7716_32x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU2] = { + .name = "as7716_32x_led::psu2", + .default_trigger = "unused", + .brightness_set = as7716_32x_led_auto_set, + .brightness_get = as7716_32x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, +}; + +static int as7716_32x_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(as7716_32x_leds); i++) { + led_classdev_suspend(&as7716_32x_leds[i]); + } + + return 0; +} + +static int as7716_32x_led_resume(struct platform_device *dev) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(as7716_32x_leds); i++) { + led_classdev_resume(&as7716_32x_leds[i]); + } + + return 0; +} + +static int as7716_32x_led_probe(struct platform_device *pdev) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(as7716_32x_leds); i++) { + ret = led_classdev_register(&pdev->dev, &as7716_32x_leds[i]); + + if (ret < 0) + { + break; + } + } + + /* Check if all LEDs were successfully registered */ + if (i != ARRAY_SIZE(as7716_32x_leds)){ + int j; + + /* only unregister the LEDs that were successfully registered */ + for (j = 0; j < i; j++) { + led_classdev_unregister(&as7716_32x_leds[i]); + } + } + + return 0; +} + +static int as7716_32x_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(as7716_32x_leds); i++) { + led_classdev_unregister(&as7716_32x_leds[i]); + } + + return 0; +} + +static struct platform_driver as7716_32x_led_driver = { + .probe = as7716_32x_led_probe, + .remove = as7716_32x_led_remove, + .suspend = as7716_32x_led_suspend, + .resume = as7716_32x_led_resume, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init as7716_32x_led_init(void) +{ + int ret; + + + ret = platform_driver_register(&as7716_32x_led_driver); + if (ret < 0) { + + goto exit; + } + + ledctl = kzalloc(sizeof(struct as7716_32x_led_data), GFP_KERNEL); + if (!ledctl) { + ret = -ENOMEM; + platform_driver_unregister(&as7716_32x_led_driver); + goto exit; + } + + mutex_init(&ledctl->update_lock); + + ledctl->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(ledctl->pdev)) { + ret = PTR_ERR(ledctl->pdev); + platform_driver_unregister(&as7716_32x_led_driver); + kfree(ledctl); + goto exit; + } + +exit: + return 0; +} + +static void __exit as7716_32x_led_exit(void) +{ + platform_device_unregister(ledctl->pdev); + platform_driver_unregister(&as7716_32x_led_driver); + kfree(ledctl); +} + +module_init(as7716_32x_led_init); +module_exit(as7716_32x_led_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7716_32x_led driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_psu.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_psu.c new file mode 100755 index 000000000000..78a58b820b2a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_psu.c @@ -0,0 +1,381 @@ +/* + * An hwmon driver for accton as7716_32x Power Module + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_MODEL_NAME 16 + +#define DC12V_FAN_DIR_OFFSET 0x34 +#define DC12V_FAN_DIR_LEN 3 + +static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_string(struct device *dev, struct device_attribute *da, char *buf); +static int as7716_32x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); +extern int as7716_32x_cpld_read (unsigned short cpld_addr, u8 reg); + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as7716_32x_psu_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + u8 status; /* Status(present/power_good) register read from CPLD */ + char model_name[MAX_MODEL_NAME+1]; /* Model name, read from eeprom */ + char fan_dir[DC12V_FAN_DIR_LEN+1]; /* DC12V fan direction */ +}; + +static struct as7716_32x_psu_data *as7716_32x_psu_update_device(struct device *dev); + +enum as7716_32x_psu_sysfs_attributes { + PSU_PRESENT, + PSU_MODEL_NAME, + PSU_POWER_GOOD, + PSU_FAN_DIR /* For DC12V only */ +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_status, NULL, PSU_PRESENT); +static SENSOR_DEVICE_ATTR(psu_model_name, S_IRUGO, show_string, NULL, PSU_MODEL_NAME); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD); +static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IRUGO, show_string, NULL, PSU_FAN_DIR); + +static struct attribute *as7716_32x_psu_attributes[] = { + &sensor_dev_attr_psu_present.dev_attr.attr, + &sensor_dev_attr_psu_model_name.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + &sensor_dev_attr_psu_fan_dir.dev_attr.attr, + NULL +}; + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7716_32x_psu_data *data = as7716_32x_psu_update_device(dev); + u8 status = 0; + + if (!data->valid) { + return -EIO; + } + + if (attr->index == PSU_PRESENT) { + status = !(data->status >> (1-data->index) & 0x1); + } + else { /* PSU_POWER_GOOD */ + status = (data->status >> (3-data->index) & 0x1); + } + + return sprintf(buf, "%d\n", status); +} + +static ssize_t show_string(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7716_32x_psu_data *data = as7716_32x_psu_update_device(dev); + char *ptr = NULL; + + if (!data->valid) { + return -EIO; + } + + if (attr->index == PSU_MODEL_NAME) { + ptr = data->model_name; + } + else { /* PSU_FAN_DIR */ + ptr = data->fan_dir; + } + + return sprintf(buf, "%s\n", ptr); +} + +static const struct attribute_group as7716_32x_psu_group = { + .attrs = as7716_32x_psu_attributes, +}; + +static int as7716_32x_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7716_32x_psu_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7716_32x_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7716_32x_psu_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7716_32x_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7716_32x_psu_remove(struct i2c_client *client) +{ + struct as7716_32x_psu_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7716_32x_psu_group); + kfree(data); + + return 0; +} + +enum psu_index +{ + as7716_32x_psu1, + as7716_32x_psu2 +}; + +static const struct i2c_device_id as7716_32x_psu_id[] = { + { "as7716_32x_psu1", as7716_32x_psu1 }, + { "as7716_32x_psu2", as7716_32x_psu2 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7716_32x_psu_id); + +static struct i2c_driver as7716_32x_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7716_32x_psu", + }, + .probe = as7716_32x_psu_probe, + .remove = as7716_32x_psu_remove, + .id_table = as7716_32x_psu_id, + .address_list = normal_i2c, +}; + +static int as7716_32x_psu_read_block(struct i2c_client *client, u8 command, u8 *data, + int data_len) +{ + int result = 0; + int retry_count = 5; + + while (retry_count) { + retry_count--; + + result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); + + if (unlikely(result < 0)) { + msleep(10); + continue; + } + + if (unlikely(result != data_len)) { + result = -EIO; + msleep(10); + continue; + } + + result = 0; + break; + } + + return result; +} + +enum psu_type { + PSU_TYPE_AC_110V, + PSU_TYPE_DC_48V, + PSU_TYPE_DC_12V +}; + +struct model_name_info { + enum psu_type type; + u8 offset; + u8 length; + char* model_name; +}; + +struct model_name_info models[] = { +{PSU_TYPE_AC_110V, 0x20, 8, "YM-2651Y"}, +{PSU_TYPE_DC_48V, 0x20, 8, "YM-2651V"}, +{PSU_TYPE_DC_12V, 0x00, 11, "PSU-12V-750"}, +}; + +static int as7716_32x_psu_model_name_get(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_psu_data *data = i2c_get_clientdata(client); + int i, status; + + for (i = 0; i < ARRAY_SIZE(models); i++) { + memset(data->model_name, 0, sizeof(data->model_name)); + + status = as7716_32x_psu_read_block(client, models[i].offset, + data->model_name, models[i].length); + if (status < 0) { + data->model_name[0] = '\0'; + dev_dbg(&client->dev, "unable to read model name from (0x%x) offset(0x%x)\n", + client->addr, models[i].offset); + return status; + } + else { + data->model_name[models[i].length] = '\0'; + } + + /* Determine if the model name is known, if not, read next index + */ + if (strncmp(data->model_name, models[i].model_name, models[i].length) == 0) { + return 0; + } + else { + data->model_name[0] = '\0'; + } + } + + return -ENODATA; +} + +static struct as7716_32x_psu_data *as7716_32x_psu_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_psu_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int status; + int power_good = 0; + + data->valid = 0; + dev_dbg(&client->dev, "Starting as7716_32x update\n"); + + /* Read psu status */ + status = as7716_32x_cpld_read(0x60, 0x2); + + if (status < 0) { + dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status); + goto exit; + } + else { + data->status = status; + } + + /* Read model name */ + memset(data->model_name, 0, sizeof(data->model_name)); + memset(data->fan_dir, 0, sizeof(data->fan_dir)); + power_good = (data->status >> (3-data->index) & 0x1); + + if (power_good) { + if (as7716_32x_psu_model_name_get(dev) < 0) { + goto exit; + } + + if (strncmp(data->model_name, + models[PSU_TYPE_DC_12V].model_name, + models[PSU_TYPE_DC_12V].length) == 0) + { + /* Read fan direction */ + + status = as7716_32x_psu_read_block(client, DC12V_FAN_DIR_OFFSET, + data->fan_dir, DC12V_FAN_DIR_LEN); + + if (status < 0) { + data->fan_dir[0] = '\0'; + dev_dbg(&client->dev, "unable to read fan direction from (0x%x) offset(0x%x)\n", + client->addr, DC12V_FAN_DIR_OFFSET); + goto exit; + } + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init as7716_32x_psu_init(void) +{ + return i2c_add_driver(&as7716_32x_psu_driver); +} + +static void __exit as7716_32x_psu_exit(void) +{ + i2c_del_driver(&as7716_32x_psu_driver); +} + +module_init(as7716_32x_psu_init); +module_exit(as7716_32x_psu_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7716_32x_psu driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_sfp.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_sfp.c new file mode 100755 index 000000000000..9c7a05632154 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_as7716_32x_sfp.c @@ -0,0 +1,365 @@ +/* + * An hwmon driver for accton as7716_32x sfp + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BIT_INDEX(i) (1UL << (i)) +#define I2C_ADDR_CPLD1 0x60 +#define I2C_ADDR_CPLD2 0x62 +#define I2C_ADDR_CPLD3 0x64 +#define CPLD1_OFFSET_QSFP_PRESET1 0x30 +#define CPLD1_OFFSET_QSFP_PRESET2 0x31 +#define CPLD1_OFFSET_QSFP_PRESET3 0x32 +#define CPLD1_OFFSET_QSFP_PRESET4 0x33 + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as7716_32x_sfp_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + int port; /* Front port index */ + char eeprom[256]; /* eeprom data */ + u32 is_present; /* present status */ +}; + +static struct as7716_32x_sfp_data *as7716_32x_sfp_update_device(struct device *dev); +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_present(struct device *dev, struct device_attribute *da,char *buf); +static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, char *buf); +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +enum as7716_32x_sfp_sysfs_attributes { + SFP_PORT_NUMBER, + SFP_IS_PRESENT, + SFP_IS_PRESENT_ALL, + SFP_EEPROM +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, SFP_PORT_NUMBER); +static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_present, NULL, SFP_IS_PRESENT); +static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_present, NULL, SFP_IS_PRESENT_ALL); +static SENSOR_DEVICE_ATTR(sfp_eeprom, S_IRUGO, show_eeprom, NULL, SFP_EEPROM); + +static struct attribute *as7716_32x_sfp_attributes[] = { + &sensor_dev_attr_sfp_port_number.dev_attr.attr, + &sensor_dev_attr_sfp_is_present.dev_attr.attr, + &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, + &sensor_dev_attr_sfp_eeprom.dev_attr.attr, + NULL +}; + +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_sfp_data *data = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", data->port+1); +} + +/* Error-check the CPLD read results. */ +#define VALIDATED_READ(_buf, _rv, _read_expr, _invert) \ +do { \ + _rv = (_read_expr); \ + if(_rv < 0) { \ + return sprintf(_buf, "READ ERROR\n"); \ + } \ + if(_invert) { \ + _rv = ~_rv; \ + } \ + _rv &= 0xFF; \ +} while(0) + +static ssize_t show_present(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + + if(attr->index == SFP_IS_PRESENT_ALL) { + int values[4]; + /* + * Report the SFP_PRESENCE status for all ports. + */ + + /* QSFP_PRESENT Ports 1-8 */ + //VALIDATED_READ(buf, values[0], accton_i2c_cpld_read(0x62, 0x9), 1); + VALIDATED_READ(buf, values[0], accton_i2c_cpld_read(I2C_ADDR_CPLD1, CPLD1_OFFSET_QSFP_PRESET1), 1); + /* QSFP_PRESENT Ports 9-16 */ + VALIDATED_READ(buf, values[1], accton_i2c_cpld_read(I2C_ADDR_CPLD1, CPLD1_OFFSET_QSFP_PRESET2), 1); + /* QSFP_PRESENT Ports 17-24 */ + VALIDATED_READ(buf, values[2], accton_i2c_cpld_read(I2C_ADDR_CPLD1, CPLD1_OFFSET_QSFP_PRESET3), 1); + /* QSFP_PRESENT Ports 25-32 */ + VALIDATED_READ(buf, values[3], accton_i2c_cpld_read(I2C_ADDR_CPLD1, CPLD1_OFFSET_QSFP_PRESET4), 1); + + /* Return values 1 -> 32 in order */ + return sprintf(buf, "%.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], values[3]); + } + else { /* SFP_IS_PRESENT */ + struct as7716_32x_sfp_data *data = as7716_32x_sfp_update_device(dev); + + if (!data->valid) { + printk("return -EIO\n"); + return -EIO; + } + + return sprintf(buf, "%d\n", data->is_present); + } +} + +static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as7716_32x_sfp_data *data = as7716_32x_sfp_update_device(dev); + + if (!data->valid) { + return 0; + } + + if (!data->is_present) { + return 0; + } + + memcpy(buf, data->eeprom, sizeof(data->eeprom)); + + return sizeof(data->eeprom); +} + +static const struct attribute_group as7716_32x_sfp_group = { + .attrs = as7716_32x_sfp_attributes, +}; + +static int as7716_32x_sfp_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7716_32x_sfp_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7716_32x_sfp_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + mutex_init(&data->update_lock); + data->port = dev_id->driver_data; + i2c_set_clientdata(client, data); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7716_32x_sfp_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: sfp '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7716_32x_sfp_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7716_32x_sfp_remove(struct i2c_client *client) +{ + struct as7716_32x_sfp_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7716_32x_sfp_group); + kfree(data); + + return 0; +} + +enum port_numbers { +as7716_32x_sfp1, as7716_32x_sfp2, as7716_32x_sfp3, as7716_32x_sfp4, +as7716_32x_sfp5, as7716_32x_sfp6, as7716_32x_sfp7, as7716_32x_sfp8, +as7716_32x_sfp9, as7716_32x_sfp10,as7716_32x_sfp11,as7716_32x_sfp12, +as7716_32x_sfp13,as7716_32x_sfp14,as7716_32x_sfp15,as7716_32x_sfp16, +as7716_32x_sfp17,as7716_32x_sfp18,as7716_32x_sfp19,as7716_32x_sfp20, +as7716_32x_sfp21,as7716_32x_sfp22,as7716_32x_sfp23,as7716_32x_sfp24, +as7716_32x_sfp25,as7716_32x_sfp26,as7716_32x_sfp27,as7716_32x_sfp28, +as7716_32x_sfp29,as7716_32x_sfp30,as7716_32x_sfp31,as7716_32x_sfp32 +}; + +static const struct i2c_device_id as7716_32x_sfp_id[] = { +{ "as7716_32x_sfp1", as7716_32x_sfp1 }, { "as7716_32x_sfp2", as7716_32x_sfp2 }, +{ "as7716_32x_sfp3", as7716_32x_sfp3 }, { "as7716_32x_sfp4", as7716_32x_sfp4 }, +{ "as7716_32x_sfp5", as7716_32x_sfp5 }, { "as7716_32x_sfp6", as7716_32x_sfp6 }, +{ "as7716_32x_sfp7", as7716_32x_sfp7 }, { "as7716_32x_sfp8", as7716_32x_sfp8 }, +{ "as7716_32x_sfp9", as7716_32x_sfp9 }, { "as7716_32x_sfp10", as7716_32x_sfp10 }, +{ "as7716_32x_sfp11", as7716_32x_sfp11 }, { "as7716_32x_sfp12", as7716_32x_sfp12 }, +{ "as7716_32x_sfp13", as7716_32x_sfp13 }, { "as7716_32x_sfp14", as7716_32x_sfp14 }, +{ "as7716_32x_sfp15", as7716_32x_sfp15 }, { "as7716_32x_sfp16", as7716_32x_sfp16 }, +{ "as7716_32x_sfp17", as7716_32x_sfp17 }, { "as7716_32x_sfp18", as7716_32x_sfp18 }, +{ "as7716_32x_sfp19", as7716_32x_sfp19 }, { "as7716_32x_sfp20", as7716_32x_sfp20 }, +{ "as7716_32x_sfp21", as7716_32x_sfp21 }, { "as7716_32x_sfp22", as7716_32x_sfp22 }, +{ "as7716_32x_sfp23", as7716_32x_sfp23 }, { "as7716_32x_sfp24", as7716_32x_sfp24 }, +{ "as7716_32x_sfp25", as7716_32x_sfp25 }, { "as7716_32x_sfp26", as7716_32x_sfp26 }, +{ "as7716_32x_sfp27", as7716_32x_sfp27 }, { "as7716_32x_sfp28", as7716_32x_sfp28 }, +{ "as7716_32x_sfp29", as7716_32x_sfp29 }, { "as7716_32x_sfp30", as7716_32x_sfp30 }, +{ "as7716_32x_sfp31", as7716_32x_sfp31 }, { "as7716_32x_sfp32", as7716_32x_sfp32 }, +{} +}; +MODULE_DEVICE_TABLE(i2c, as7716_32x_sfp_id); + +static struct i2c_driver as7716_32x_sfp_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7716_32x_sfp", + }, + .probe = as7716_32x_sfp_probe, + .remove = as7716_32x_sfp_remove, + .id_table = as7716_32x_sfp_id, + .address_list = normal_i2c, +}; + +static int as7716_32x_sfp_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; +} + +static struct as7716_32x_sfp_data *as7716_32x_sfp_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_sfp_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int status = -1; + int i = 0; + u8 cpld_reg = 0x30 + (data->port/8); + + data->valid = 0; + + /* Read present status of the specified port number */ + data->is_present = 0; + status = accton_i2c_cpld_read(I2C_ADDR_CPLD1, cpld_reg); + + if (status < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", I2C_ADDR_CPLD1, cpld_reg, status); + goto exit; + } + + data->is_present = (status & (1 << (data->port % 8))) ? 0 : 1; + printk("data->is_present=%d, data->port=%d, status=0x%x\n",data->is_present, data->port, status); + /* Read eeprom data based on port number */ + memset(data->eeprom, 0, sizeof(data->eeprom)); + + /* Check if the port is present */ + if (data->is_present) { + /* read eeprom */ + for (i = 0; i < sizeof(data->eeprom)/I2C_SMBUS_BLOCK_MAX; i++) { + status = as7716_32x_sfp_read_block(client, i*I2C_SMBUS_BLOCK_MAX, + data->eeprom+(i*I2C_SMBUS_BLOCK_MAX), + I2C_SMBUS_BLOCK_MAX); + if (status < 0) { + printk("unable to read eeprom from port(%d)\n", data->port); + dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", data->port); + goto exit; + } + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init as7716_32x_sfp_init(void) +{ + //extern int platform_accton_as7716_32x(void); + //if (!platform_accton_as7716_32x()) { +// return -ENODEV; + //} + + return i2c_add_driver(&as7716_32x_sfp_driver); +} + +static void __exit as7716_32x_sfp_exit(void) +{ + i2c_del_driver(&as7716_32x_sfp_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton as7716_32x_sfp driver"); +MODULE_LICENSE("GPL"); + +module_init(as7716_32x_sfp_init); +module_exit(as7716_32x_sfp_exit); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_i2c_cpld.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_i2c_cpld.c new file mode 100755 index 000000000000..46d1e2773287 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/accton_i2c_cpld.c @@ -0,0 +1,259 @@ +/* + * A hwmon driver for the accton_i2c_cpld + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CPLD_VERSION_REG 0x1 + +enum as5712_54x_cpld_sysfs_attributes { + CPLD_READ_VERSION, + CPLD_BYTE_ACCESS, + CPLD_DUMP_ALL, +}; + +static ssize_t read_cpld_version(struct device *dev, struct device_attribute *da, + char *buf); +int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); + + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +/* Addresses scanned for accton_i2c_cpld + */ +static const unsigned short normal_i2c[] = { 0x31, 0x35, 0x60, 0x61, 0x62, I2C_CLIENT_END }; + +static SENSOR_DEVICE_ATTR(cpld_get_version, S_IRUGO, read_cpld_version, NULL, CPLD_READ_VERSION); + +static struct attribute *as5712_54x_cpld_attributes[] = { + &sensor_dev_attr_cpld_get_version.dev_attr.attr, + NULL +}; + +static const struct attribute_group as5712_54x_cpld_group = { + .attrs = as5712_54x_cpld_attributes, +}; + +static ssize_t read_cpld_version(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + unsigned short cpld_reg = CPLD_VERSION_REG; + u8 reg; + + if(attr->index == CPLD_READ_VERSION) { + reg = accton_i2c_cpld_read(client->addr, cpld_reg); + return sprintf(buf, "%02x\n",reg); + } + return -1 ; +} + + +static void accton_i2c_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void accton_i2c_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + + mutex_unlock(&list_lock); +} + +static int accton_i2c_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_dbg(&client->dev, "i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as5712_54x_cpld_group); + if (status) { + goto exit; + } + + dev_info(&client->dev, "chip found\n"); + accton_i2c_cpld_add_client(client); + + return 0; +exit: + return status; +} + +static int accton_i2c_cpld_remove(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &as5712_54x_cpld_group); + + accton_i2c_cpld_remove_client(client); + return 0; +} + +static const struct i2c_device_id accton_i2c_cpld_id[] = { + { "accton_i2c_cpld", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, accton_i2c_cpld_id); + +static struct i2c_driver accton_i2c_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "accton_i2c_cpld", + }, + .probe = accton_i2c_cpld_probe, + .remove = accton_i2c_cpld_remove, + .id_table = accton_i2c_cpld_id, + .address_list = normal_i2c, +}; + +int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = i2c_smbus_read_byte_data(cpld_node->client, reg); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(accton_i2c_cpld_read); + +int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = i2c_smbus_write_byte_data(cpld_node->client, reg, value); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(accton_i2c_cpld_write); + +static int __init accton_i2c_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&accton_i2c_cpld_driver); +} + +static void __exit accton_i2c_cpld_exit(void) +{ + i2c_del_driver(&accton_i2c_cpld_driver); +} +/* +static struct dmi_system_id as7712_dmi_table[] = { + { + .ident = "Accton AS7712", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS7712"), + }, + } +}; + +int platform_accton_as7712_32x(void) +{ + return dmi_check_system(as7712_dmi_table); +} +EXPORT_SYMBOL(platform_accton_as7712_32x); +*/ +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_i2c_cpld driver"); +MODULE_LICENSE("GPL"); + +module_init(accton_i2c_cpld_init); +module_exit(accton_i2c_cpld_exit); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/cpr_4011_4mxx.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/cpr_4011_4mxx.c new file mode 100755 index 000000000000..30bea914d589 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/cpr_4011_4mxx.c @@ -0,0 +1,400 @@ +/* + * An hwmon driver for the CPR-4011-4Mxx Redundant Power Module + * + * Copyright (C) Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * 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. + */ +#if 0 +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_FAN_DUTY_CYCLE 100 + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x3c, 0x3d, 0x3e, 0x3f, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct cpr_4011_4mxx_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 vout_mode; /* Register value */ + u16 v_in; /* Register value */ + u16 v_out; /* Register value */ + u16 i_in; /* Register value */ + u16 i_out; /* Register value */ + u16 p_in; /* Register value */ + u16 p_out; /* Register value */ + u16 temp_input[2]; /* Register value */ + u8 fan_fault; /* Register value */ + u16 fan_duty_cycle[2]; /* Register value */ + u16 fan_speed[2]; /* Register value */ +}; + +static ssize_t show_linear(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_vout(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da, const char *buf, size_t count); +static int cpr_4011_4mxx_write_word(struct i2c_client *client, u8 reg, u16 value); +static struct cpr_4011_4mxx_data *cpr_4011_4mxx_update_device(struct device *dev); + +enum cpr_4011_4mxx_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, +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_v_in, S_IRUGO, show_linear, NULL, PSU_V_IN); +static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, show_vout, NULL, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(psu_i_in, S_IRUGO, show_linear, NULL, PSU_I_IN); +static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, show_linear, NULL, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(psu_p_in, S_IRUGO, show_linear, NULL, PSU_P_IN); +static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, show_linear, NULL, PSU_P_OUT); +static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_fan_fault, NULL, PSU_FAN1_FAULT); +static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IWUSR | S_IRUGO, show_linear, set_fan_duty_cycle, PSU_FAN1_DUTY_CYCLE); +static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); + +static struct attribute *cpr_4011_4mxx_attributes[] = { + &sensor_dev_attr_psu_v_in.dev_attr.attr, + &sensor_dev_attr_psu_v_out.dev_attr.attr, + &sensor_dev_attr_psu_i_in.dev_attr.attr, + &sensor_dev_attr_psu_i_out.dev_attr.attr, + &sensor_dev_attr_psu_p_in.dev_attr.attr, + &sensor_dev_attr_psu_p_out.dev_attr.attr, + &sensor_dev_attr_psu_temp1_input.dev_attr.attr, + &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, + &sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr, + &sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr, + NULL +}; + +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(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpr_4011_4mxx_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; + + mutex_lock(&data->update_lock); + data->fan_duty_cycle[nr] = speed; + cpr_4011_4mxx_write_word(client, 0x3B + nr, data->fan_duty_cycle[nr]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_linear(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpr_4011_4mxx_data *data = cpr_4011_4mxx_update_device(dev); + + u16 value = 0; + int exponent, mantissa; + int multiplier = 1000; + + switch (attr->index) { + case PSU_V_IN: + value = data->v_in; + break; + case PSU_I_IN: + value = data->i_in; + break; + case PSU_I_OUT: + value = data->i_out; + break; + case PSU_P_IN: + value = data->p_in; + break; + case PSU_P_OUT: + value = data->p_out; + break; + case PSU_TEMP1_INPUT: + value = data->temp_input[0]; + break; + case PSU_FAN1_DUTY_CYCLE: + multiplier = 1; + value = data->fan_duty_cycle[0]; + break; + case PSU_FAN1_SPEED: + multiplier = 1; + value = data->fan_speed[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 show_fan_fault(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpr_4011_4mxx_data *data = cpr_4011_4mxx_update_device(dev); + + u8 shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6; + + return sprintf(buf, "%d\n", data->fan_fault >> shift); +} + +static ssize_t show_vout(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct cpr_4011_4mxx_data *data = cpr_4011_4mxx_update_device(dev); + int exponent, mantissa; + int multiplier = 1000; + + exponent = two_complement_to_int(data->vout_mode, 5, 0x1f); + mantissa = data->v_out; + + return (exponent > 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) : + sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); +} + +static const struct attribute_group cpr_4011_4mxx_group = { + .attrs = cpr_4011_4mxx_attributes, +}; + +static int cpr_4011_4mxx_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct cpr_4011_4mxx_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(struct cpr_4011_4mxx_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, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &cpr_4011_4mxx_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &cpr_4011_4mxx_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int cpr_4011_4mxx_remove(struct i2c_client *client) +{ + struct cpr_4011_4mxx_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &cpr_4011_4mxx_group); + kfree(data); + + return 0; +} + +static const struct i2c_device_id cpr_4011_4mxx_id[] = { + { "cpr_4011_4mxx", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, cpr_4011_4mxx_id); + +static struct i2c_driver cpr_4011_4mxx_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "cpr_4011_4mxx", + }, + .probe = cpr_4011_4mxx_probe, + .remove = cpr_4011_4mxx_remove, + .id_table = cpr_4011_4mxx_id, + .address_list = normal_i2c, +}; + +static int cpr_4011_4mxx_read_byte(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int cpr_4011_4mxx_read_word(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_data(client, reg); +} + +static int cpr_4011_4mxx_write_word(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_word_data(client, reg, value); +} + +struct reg_data_byte { + u8 reg; + u8 *value; +}; + +struct reg_data_word { + u8 reg; + u16 *value; +}; + +static struct cpr_4011_4mxx_data *cpr_4011_4mxx_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpr_4011_4mxx_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int i, status; + struct reg_data_byte regs_byte[] = { {0x20, &data->vout_mode}, + {0x81, &data->fan_fault}}; + struct reg_data_word regs_word[] = { {0x88, &data->v_in}, + {0x8b, &data->v_out}, + {0x89, &data->i_in}, + {0x8c, &data->i_out}, + {0x96, &data->p_out}, + {0x97, &data->p_in}, + {0x8d, &(data->temp_input[0])}, + {0x8e, &(data->temp_input[1])}, + {0x3b, &(data->fan_duty_cycle[0])}, + {0x3c, &(data->fan_duty_cycle[1])}, + {0x90, &(data->fan_speed[0])}, + {0x91, &(data->fan_speed[1])}}; + + dev_dbg(&client->dev, "Starting cpr_4011_4mxx update\n"); + + /* Read byte data */ + for (i = 0; i < ARRAY_SIZE(regs_byte); i++) { + status = cpr_4011_4mxx_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; + } + } + + /* Read word data */ + for (i = 0; i < ARRAY_SIZE(regs_word); i++) { + status = cpr_4011_4mxx_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; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init cpr_4011_4mxx_init(void) +{ + return i2c_add_driver(&cpr_4011_4mxx_driver); +} + +static void __exit cpr_4011_4mxx_exit(void) +{ + i2c_del_driver(&cpr_4011_4mxx_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("CPR_4011_4MXX driver"); +MODULE_LICENSE("GPL"); + +module_init(cpr_4011_4mxx_init); +module_exit(cpr_4011_4mxx_exit); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/optoe.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/optoe.c new file mode 100755 index 000000000000..16be2fef89b1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/optoe.c @@ -0,0 +1,1148 @@ +/* + * optoe.c - A driver to read and write the EEPROM on optical transceivers + * (SFP, QSFP and similar I2C based devices) + * + * Copyright (C) 2014 Cumulus networks Inc. + * Copyright (C) 2017 Finisar Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Freeoftware Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * Description: + * a) Optical transceiver EEPROM read/write transactions are just like + * the at24 eeproms managed by the at24.c i2c driver + * b) The register/memory layout is up to 256 128 byte pages defined by + * a "pages valid" register and switched via a "page select" + * register as explained in below diagram. + * c) 256 bytes are mapped at a time. 'Lower page 00h' is the first 128 + * bytes of address space, and always references the same + * location, independent of the page select register. + * All mapped pages are mapped into the upper 128 bytes + * (offset 128-255) of the i2c address. + * d) Devices with one I2C address (eg QSFP) use I2C address 0x50 + * (A0h in the spec), and map all pages in the upper 128 bytes + * of that address. + * e) Devices with two I2C addresses (eg SFP) have 256 bytes of data + * at I2C address 0x50, and 256 bytes of data at I2C address + * 0x51 (A2h in the spec). Page selection and paged access + * only apply to this second I2C address (0x51). + * e) The address space is presented, by the driver, as a linear + * address space. For devices with one I2C client at address + * 0x50 (eg QSFP), offset 0-127 are in the lower + * half of address 50/A0h/client[0]. Offset 128-255 are in + * page 0, 256-383 are page 1, etc. More generally, offset + * 'n' resides in page (n/128)-1. ('page -1' is the lower + * half, offset 0-127). + * f) For devices with two I2C clients at address 0x50 and 0x51 (eg SFP), + * the address space places offset 0-127 in the lower + * half of 50/A0/client[0], offset 128-255 in the upper + * half. Offset 256-383 is in the lower half of 51/A2/client[1]. + * Offset 384-511 is in page 0, in the upper half of 51/A2/... + * Offset 512-639 is in page 1, in the upper half of 51/A2/... + * Offset 'n' is in page (n/128)-3 (for n > 383) + * + * One I2c addressed (eg QSFP) Memory Map + * + * 2-Wire Serial Address: 1010000x + * + * Lower Page 00h (128 bytes) + * ===================== + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * |Page Select Byte(127)| + * ===================== + * | + * | + * | + * | + * V + * ------------------------------------------------------------ + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * V V V V + * ------------ -------------- --------------- -------------- + * | | | | | | | | + * | Upper | | Upper | | Upper | | Upper | + * | Page 00h | | Page 01h | | Page 02h | | Page 03h | + * | | | (Optional) | | (Optional) | | (Optional | + * | | | | | | | for Cable | + * | | | | | | | Assemblies) | + * | ID | | AST | | User | | | + * | Fields | | Table | | EEPROM Data | | | + * | | | | | | | | + * | | | | | | | | + * | | | | | | | | + * ------------ -------------- --------------- -------------- + * + * The SFF 8436 (QSFP) spec only defines the 4 pages described above. + * In anticipation of future applications and devices, this driver + * supports access to the full architected range, 256 pages. + * + **/ + +/* #define DEBUG 1 */ + +#undef EEPROM_CLASS +#ifdef CONFIG_EEPROM_CLASS +#define EEPROM_CLASS +#endif +#ifdef CONFIG_EEPROM_CLASS_MODULE +#define EEPROM_CLASS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The optoe driver is for read/write access to the EEPROM on standard + * I2C based optical transceivers (SFP, QSFP, etc) + * + * While based on the at24 driver, it eliminates code that supports other + * types of I2C EEPROMs, and adds support for pages accessed through the + * page-select register at offset 127. + */ + +struct optoe_platform_data { + u32 byte_len; /* size (sum of all addr) */ + u16 page_size; /* for writes */ + u8 flags; + + void (*setup)(struct memory_accessor *, void *context); + void *context; +#ifdef EEPROM_CLASS + struct eeprom_platform_data *eeprom_data; /* extra data for the eeprom_class */ +#endif +}; + +#ifdef EEPROM_CLASS +#include +#endif + +#include + +/* fundamental unit of addressing for EEPROM */ +#define OPTOE_PAGE_SIZE 128 +/* + * Single address devices (eg QSFP) have 256 pages, plus the unpaged + * low 128 bytes. If the device does not support paging, it is + * only 2 'pages' long. + */ +#define OPTOE_ARCH_PAGES 256 +#define ONE_ADDR_EEPROM_SIZE ((1 + OPTOE_ARCH_PAGES) * OPTOE_PAGE_SIZE) +#define ONE_ADDR_EEPROM_UNPAGED_SIZE (2 * OPTOE_PAGE_SIZE) +/* + * Dual address devices (eg SFP) have 256 pages, plus the unpaged + * low 128 bytes, plus 256 bytes at 0x50. If the device does not + * support paging, it is 4 'pages' long. + */ +#define TWO_ADDR_EEPROM_SIZE ((3 + OPTOE_ARCH_PAGES) * OPTOE_PAGE_SIZE) +#define TWO_ADDR_EEPROM_UNPAGED_SIZE (4 * OPTOE_PAGE_SIZE) + +/* a few constants to find our way around the EEPROM */ +#define OPTOE_PAGE_SELECT_REG 0x7F +#define ONE_ADDR_PAGEABLE_REG 0x02 +#define ONE_ADDR_NOT_PAGEABLE (1<<2) +#define TWO_ADDR_PAGEABLE_REG 0x40 +#define TWO_ADDR_PAGEABLE (1<<4) +#define OPTOE_ID_REG 0 + +/* The maximum length of a port name */ +#define MAX_PORT_NAME_LEN 20 +struct optoe_data { + struct optoe_platform_data chip; + struct memory_accessor macc; + int use_smbus; + char port_name[MAX_PORT_NAME_LEN]; + + /* + * Lock protects against activities from other Linux tasks, + * but not from changes by other I2C masters. + */ + struct mutex lock; + struct bin_attribute bin; + struct attribute_group attr_group; + + u8 *writebuf; + unsigned write_max; + + unsigned num_addresses; + +#ifdef EEPROM_CLASS + struct eeprom_device *eeprom_dev; +#endif + + /* dev_class: ONE_ADDR (QSFP) or TWO_ADDR (SFP) */ + int dev_class; + + struct i2c_client *client[]; +}; + +typedef enum optoe_opcode { + OPTOE_READ_OP = 0, + OPTOE_WRITE_OP = 1 +} optoe_opcode_e; + +/* + * 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 = OPTOE_PAGE_SIZE; + +/* + * 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; + +/* + * flags to distinguish one-address (QSFP family) from two-address (SFP family) + * If the family is not known, figure it out when the device is accessed + */ +#define ONE_ADDR 1 +#define TWO_ADDR 2 + +static const struct i2c_device_id optoe_ids[] = { + { "optoe1", ONE_ADDR }, + { "optoe2", TWO_ADDR }, + { "sff8436", ONE_ADDR }, + { "24c04", TWO_ADDR }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, optoe_ids); + +/*-------------------------------------------------------------------------*/ +/* + * This routine computes the addressing information to be used for + * a given r/w request. + * + * Task is to calculate the client (0 = i2c addr 50, 1 = i2c addr 51), + * the page, and the offset. + * + * Handles both single address (eg QSFP) and two address (eg SFP). + * For SFP, offset 0-255 are on client[0], >255 is on client[1] + * Offset 256-383 are on the lower half of client[1] + * Pages are accessible on the upper half of client[1]. + * Offset >383 are in 128 byte pages mapped into the upper half + * + * For QSFP, all offsets are on client[0] + * offset 0-127 are on the lower half of client[0] (no paging) + * Pages are accessible on the upper half of client[1]. + * Offset >127 are in 128 byte pages mapped into the upper half + * + * Callers must not read/write beyond the end of a client or a page + * without recomputing the client/page. Hence offset (within page) + * plus length must be less than or equal to 128. (Note that this + * routine does not have access to the length of the call, hence + * cannot do the validity check.) + * + * Offset within Lower Page 00h and Upper Page 00h are not recomputed + */ + +static uint8_t optoe_translate_offset(struct optoe_data *optoe, + loff_t *offset, struct i2c_client **client) +{ + unsigned page = 0; + + *client = optoe->client[0]; + + /* if SFP style, offset > 255, shift to i2c addr 0x51 */ + if (optoe->dev_class == TWO_ADDR) { + if (*offset > 255) { + /* like QSFP, but shifted to client[1] */ + *client = optoe->client[1]; + *offset -= 256; + } + } + + /* + * if offset is in the range 0-128... + * page doesn't matter (using lower half), return 0. + * offset is already correct (don't add 128 to get to paged area) + */ + if (*offset < OPTOE_PAGE_SIZE) + return page; + + /* note, page will always be positive since *offset >= 128 */ + page = (*offset >> 7)-1; + /* 0x80 places the offset in the top half, offset is last 7 bits */ + *offset = OPTOE_PAGE_SIZE + (*offset & 0x7f); + + return page; /* note also returning client and offset */ +} + +static ssize_t optoe_eeprom_read(struct optoe_data *optoe, + struct i2c_client *client, + char *buf, unsigned offset, size_t count) +{ + struct i2c_msg msg[2]; + u8 msgbuf[2]; + unsigned long timeout, read_time; + int status, i; + + memset(msg, 0, sizeof(msg)); + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* + * When we have a better choice than SMBus calls, use a + * combined I2C message. Write address; then read up to + * io_limit data bytes. msgbuf is u8 and will cast to our + * needs. + */ + i = 0; + msgbuf[i++] = offset; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + read_time = jiffies; + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_read_i2c_block_data(client, offset, + count, buf); + break; + case I2C_SMBUS_WORD_DATA: + status = i2c_smbus_read_word_data(client, offset); + if (status >= 0) { + buf[0] = status & 0xff; + if (count == 2) + buf[1] = status >> 8; + status = count; + } + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_read_byte_data(client, offset); + if (status >= 0) { + buf[0] = status; + status = count; + } + break; + default: + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + status = count; + } + + dev_dbg(&client->dev, "eeprom read %zu@%d --> %d (%ld)\n", + count, offset, status, jiffies); + + if (status == count) /* happy path */ + return count; + + if (status == -ENXIO) /* no module present */ + return status; + + /* REVISIT: at HZ=100, this is sloooow */ + msleep(1); + } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; +} + +static ssize_t optoe_eeprom_write(struct optoe_data *optoe, + struct i2c_client *client, + const char *buf, + unsigned offset, size_t count) +{ + struct i2c_msg msg; + ssize_t status; + unsigned long timeout, write_time; + unsigned next_page_start; + int i = 0; + + /* write max is at most a page + * (In this driver, write_max is actually one byte!) + */ + if (count > optoe->write_max) + count = optoe->write_max; + + /* shorten count if necessary to avoid crossing page boundary */ + next_page_start = roundup(offset + 1, OPTOE_PAGE_SIZE); + if (offset + count > next_page_start) + count = next_page_start - offset; + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* If we'll use I2C calls for I/O, set up the message */ + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = optoe->writebuf; + + msg.buf[i++] = offset; + memcpy(&msg.buf[i], buf, count); + msg.len = i + count; + break; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + write_time = jiffies; + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_write_i2c_block_data(client, + offset, count, buf); + if (status == 0) + status = count; + break; + case I2C_SMBUS_WORD_DATA: + if (count == 2) { + status = i2c_smbus_write_word_data(client, + offset, (u16)((buf[0])|(buf[1] << 8))); + } else { + /* count = 1 */ + status = i2c_smbus_write_byte_data(client, + offset, buf[0]); + } + if (status == 0) + status = count; + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_write_byte_data(client, offset, + buf[0]); + if (status == 0) + status = count; + break; + default: + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + break; + } + + dev_dbg(&client->dev, "eeprom write %zu@%d --> %ld (%lu)\n", + count, offset, (long int) status, jiffies); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + msleep(1); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + + +static ssize_t optoe_eeprom_update_client(struct optoe_data *optoe, + char *buf, loff_t off, + size_t count, optoe_opcode_e opcode) +{ + struct i2c_client *client; + ssize_t retval = 0; + uint8_t page = 0; + loff_t phy_offset = off; + int ret = 0; + + page = optoe_translate_offset(optoe, &phy_offset, &client); + dev_dbg(&client->dev, + "optoe_eeprom_update_client off %lld page:%d phy_offset:%lld, count:%ld, opcode:%d\n", + off, page, phy_offset, (long int) count, opcode); + if (page > 0) { + ret = optoe_eeprom_write(optoe, client, &page, + OPTOE_PAGE_SELECT_REG, 1); + if (ret < 0) { + dev_dbg(&client->dev, + "Write page register for page %d failed ret:%d!\n", + page, ret); + return ret; + } + } + + while (count) { + ssize_t status; + + if (opcode == OPTOE_READ_OP) { + status = optoe_eeprom_read(optoe, client, + buf, phy_offset, count); + } else { + status = optoe_eeprom_write(optoe, client, + buf, phy_offset, count); + } + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + buf += status; + phy_offset += status; + count -= status; + retval += status; + } + + + if (page > 0) { + /* return the page register to page 0 (why?) */ + page = 0; + ret = optoe_eeprom_write(optoe, client, &page, + OPTOE_PAGE_SELECT_REG, 1); + if (ret < 0) { + dev_err(&client->dev, + "Restore page register to 0 failed:%d!\n", ret); + /* error only if nothing has been transferred */ + if (retval == 0) retval = ret; + } + } + return retval; +} + +/* + * Figure out if this access is within the range of supported pages. + * Note this is called on every access because we don't know if the + * module has been replaced since the last call. + * If/when modules support more pages, this is the routine to update + * to validate and allow access to additional pages. + * + * Returns updated len for this access: + * - entire access is legal, original len is returned. + * - access begins legal but is too long, len is truncated to fit. + * - initial offset exceeds supported pages, return -EINVAL + */ +static ssize_t optoe_page_legal(struct optoe_data *optoe, + loff_t off, size_t len) +{ + struct i2c_client *client = optoe->client[0]; + u8 regval; + int status; + size_t maxlen; + + if (off < 0) return -EINVAL; + if (optoe->dev_class == TWO_ADDR) { + /* SFP case */ + /* if no pages needed, we're good */ + if ((off + len) <= TWO_ADDR_EEPROM_UNPAGED_SIZE) return len; + /* if offset exceeds possible pages, we're not good */ + if (off >= TWO_ADDR_EEPROM_SIZE) return -EINVAL; + /* in between, are pages supported? */ + status = optoe_eeprom_read(optoe, client, ®val, + TWO_ADDR_PAGEABLE_REG, 1); + if (status < 0) return status; /* error out (no module?) */ + if (regval & TWO_ADDR_PAGEABLE) { + /* Pages supported, trim len to the end of pages */ + maxlen = TWO_ADDR_EEPROM_SIZE - off; + } else { + /* pages not supported, trim len to unpaged size */ + if (off >= TWO_ADDR_EEPROM_UNPAGED_SIZE) return -EINVAL; + maxlen = TWO_ADDR_EEPROM_UNPAGED_SIZE - off; + } + len = (len > maxlen) ? maxlen : len; + dev_dbg(&client->dev, + "page_legal, SFP, off %lld len %ld\n", + off, (long int) len); + } else { + /* QSFP case */ + /* if no pages needed, we're good */ + if ((off + len) <= ONE_ADDR_EEPROM_UNPAGED_SIZE) return len; + /* if offset exceeds possible pages, we're not good */ + if (off >= ONE_ADDR_EEPROM_SIZE) return -EINVAL; + /* in between, are pages supported? */ + status = optoe_eeprom_read(optoe, client, ®val, + ONE_ADDR_PAGEABLE_REG, 1); + if (status < 0) return status; /* error out (no module?) */ + if (regval & ONE_ADDR_NOT_PAGEABLE) { + /* pages not supported, trim len to unpaged size */ + if (off >= ONE_ADDR_EEPROM_UNPAGED_SIZE) return -EINVAL; + maxlen = ONE_ADDR_EEPROM_UNPAGED_SIZE - off; + } else { + /* Pages supported, trim len to the end of pages */ + maxlen = ONE_ADDR_EEPROM_SIZE - off; + } + len = (len > maxlen) ? maxlen : len; + dev_dbg(&client->dev, + "page_legal, QSFP, off %lld len %ld\n", + off, (long int) len); + } + return len; +} + +static ssize_t optoe_read_write(struct optoe_data *optoe, + char *buf, loff_t off, size_t len, optoe_opcode_e opcode) +{ + struct i2c_client *client = optoe->client[0]; + int chunk; + int status = 0; + ssize_t retval; + size_t pending_len = 0, chunk_len = 0; + loff_t chunk_offset = 0, chunk_start_offset = 0; + + dev_dbg(&client->dev, + "optoe_read_write: off %lld len:%ld, opcode:%s\n", + off, (long int) len, (opcode == OPTOE_READ_OP) ? "r": "w"); + if (unlikely(!len)) + return len; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&optoe->lock); + + /* + * Confirm this access fits within the device suppored addr range + */ + status = optoe_page_legal(optoe, off, len); + if (status < 0) { + goto err; + } + len = status; + + /* + * For each (128 byte) chunk involved in this request, issue a + * separate call to sff_eeprom_update_client(), to + * ensure that each access recalculates the client/page + * and writes the page register as needed. + * Note that chunk to page mapping is confusing, is different for + * QSFP and SFP, and never needs to be done. Don't try! + */ + pending_len = len; /* amount remaining to transfer */ + retval = 0; /* amount transferred */ + for (chunk = off >> 7; chunk <= (off + len - 1) >> 7; chunk++) { + + /* + * Compute the offset and number of bytes to be read/write + * + * 1. start at offset 0 (within the chunk), and read/write + * the entire chunk + * 2. start at offset 0 (within the chunk) and read/write less + * than entire chunk + * 3. start at an offset not equal to 0 and read/write the rest + * of the chunk + * 4. start at an offset not equal to 0 and read/write less than + * (end of chunk - offset) + */ + chunk_start_offset = chunk * OPTOE_PAGE_SIZE; + + if (chunk_start_offset < off) { + chunk_offset = off; + if ((off + pending_len) < (chunk_start_offset + + OPTOE_PAGE_SIZE)) + chunk_len = pending_len; + else + chunk_len = OPTOE_PAGE_SIZE - off; + } else { + chunk_offset = chunk_start_offset; + if (pending_len > OPTOE_PAGE_SIZE) + chunk_len = OPTOE_PAGE_SIZE; + else + chunk_len = pending_len; + } + + dev_dbg(&client->dev, + "sff_r/w: off %lld, len %ld, chunk_start_offset %lld, chunk_offset %lld, chunk_len %ld, pending_len %ld\n", + off, (long int) len, chunk_start_offset, chunk_offset, + (long int) chunk_len, (long int) pending_len); + + /* + * note: chunk_offset is from the start of the EEPROM, + * not the start of the chunk + */ + status = optoe_eeprom_update_client(optoe, buf, + chunk_offset, chunk_len, opcode); + if (status != chunk_len) { + /* This is another 'no device present' path */ + dev_dbg(&client->dev, + "optoe_update_client for chunk %d chunk_offset %lld chunk_len %ld failed %d!\n", + chunk, chunk_offset, (long int) chunk_len, status); + goto err; + } + buf += status; + pending_len -= status; + retval += status; + } + mutex_unlock(&optoe->lock); + + return retval; + +err: + mutex_unlock(&optoe->lock); + + return status; +} + +static ssize_t optoe_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, + struct device, kobj)); + struct optoe_data *optoe = i2c_get_clientdata(client); + + return optoe_read_write(optoe, buf, off, count, OPTOE_READ_OP); +} + + +static ssize_t optoe_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, + struct device, kobj)); + struct optoe_data *optoe = i2c_get_clientdata(client); + + return optoe_read_write(optoe, buf, off, count, OPTOE_WRITE_OP); +} +/*-------------------------------------------------------------------------*/ + +/* + * 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 optoe_macc_read(struct memory_accessor *macc, + char *buf, off_t offset, size_t count) +{ + struct optoe_data *optoe = container_of(macc, + struct optoe_data, macc); + + return optoe_read_write(optoe, buf, offset, count, OPTOE_READ_OP); +} + +static ssize_t optoe_macc_write(struct memory_accessor *macc, + const char *buf, off_t offset, size_t count) +{ + struct optoe_data *optoe = container_of(macc, + struct optoe_data, macc); + + return optoe_read_write(optoe, (char *) buf, offset, + count, OPTOE_WRITE_OP); +} + +/*-------------------------------------------------------------------------*/ + +static int optoe_remove(struct i2c_client *client) +{ + struct optoe_data *optoe; + int i; + + optoe = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &optoe->attr_group); + sysfs_remove_bin_file(&client->dev.kobj, &optoe->bin); + + for (i = 1; i < optoe->num_addresses; i++) + i2c_unregister_device(optoe->client[i]); + +#ifdef EEPROM_CLASS + eeprom_device_unregister(optoe->eeprom_dev); +#endif + + kfree(optoe->writebuf); + kfree(optoe); + return 0; +} + +static ssize_t show_port_name(struct device *dev, + struct device_attribute *dattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + ssize_t count; + + mutex_lock(&optoe->lock); + count = sprintf(buf, "%s\n", optoe->port_name); + mutex_unlock(&optoe->lock); + + return count; +} + +static ssize_t set_port_name(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + char port_name[MAX_PORT_NAME_LEN]; + + /* no checking, this value is not used except by show_port_name */ + + if (sscanf(buf, "%19s", port_name) != 1) + return -EINVAL; + + mutex_lock(&optoe->lock); + strcpy(optoe->port_name, port_name); + mutex_unlock(&optoe->lock); + + return count; +} + +static DEVICE_ATTR(port_name, S_IRUGO | S_IWUSR, + show_port_name, set_port_name); + +static ssize_t show_dev_class(struct device *dev, + struct device_attribute *dattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + ssize_t count; + + mutex_lock(&optoe->lock); + count = sprintf(buf, "%d\n", optoe->dev_class); + mutex_unlock(&optoe->lock); + + return count; +} + +static ssize_t set_dev_class(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + int dev_class; + + /* + * dev_class is actually the number of sfp ports used, thus + * legal values are "1" (QSFP class) and "2" (SFP class) + */ + if (sscanf(buf, "%d", &dev_class) != 1 || + dev_class < 1 || dev_class > 2) + return -EINVAL; + + mutex_lock(&optoe->lock); + optoe->dev_class = dev_class; + mutex_unlock(&optoe->lock); + + return count; +} + +static DEVICE_ATTR(dev_class, S_IRUGO | S_IWUSR, + show_dev_class, set_dev_class); + +static struct attribute *optoe_attrs[] = { + &dev_attr_port_name.attr, + &dev_attr_dev_class.attr, + NULL, +}; + +static struct attribute_group optoe_attr_group = { + .attrs = optoe_attrs, +}; + +static int optoe_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + int use_smbus = 0; + struct optoe_platform_data chip; + struct optoe_data *optoe; + int num_addresses = 0; + int i = 0; + + if (client->addr != 0x50) { + dev_dbg(&client->dev, "probe, bad i2c addr: 0x%x\n", + client->addr); + err = -EINVAL; + goto exit; + } + + if (client->dev.platform_data) { + chip = *(struct optoe_platform_data *)client->dev.platform_data; + dev_dbg(&client->dev, "probe, chip provided, flags:0x%x; name: %s\n", chip.flags, client->name); + } else { + if (!id->driver_data) { + err = -ENODEV; + goto exit; + } + dev_dbg(&client->dev, "probe, building chip\n"); + chip.flags = 0; + chip.setup = NULL; + chip.context = NULL; +#ifdef EEPROM_CLASS + chip.eeprom_data = NULL; +#endif + } + + /* Use I2C operations unless we're stuck with SMBus extensions. */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + use_smbus = I2C_SMBUS_I2C_BLOCK_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + use_smbus = I2C_SMBUS_WORD_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + use_smbus = I2C_SMBUS_BYTE_DATA; + } else { + err = -EPFNOSUPPORT; + goto exit; + } + } + + + /* + * Make room for two i2c clients + */ + num_addresses = 2; + + optoe = kzalloc(sizeof(struct optoe_data) + + num_addresses * sizeof(struct i2c_client *), + GFP_KERNEL); + if (!optoe) { + err = -ENOMEM; + goto exit; + } + + mutex_init(&optoe->lock); + + /* determine whether this is a one-address or two-address module */ + if ((strcmp(client->name, "optoe1") == 0) || + (strcmp(client->name, "sff8436") == 0)) { + /* one-address (eg QSFP) family */ + optoe->dev_class = ONE_ADDR; + chip.byte_len = ONE_ADDR_EEPROM_SIZE; + num_addresses = 1; + } else if ((strcmp(client->name, "optoe2") == 0) || + (strcmp(client->name, "24c04") == 0)) { + /* SFP family */ + optoe->dev_class = TWO_ADDR; + chip.byte_len = TWO_ADDR_EEPROM_SIZE; + } else { /* those were the only two choices */ + err = -EINVAL; + goto exit; + } + + dev_dbg(&client->dev, "dev_class: %d\n", optoe->dev_class); + optoe->use_smbus = use_smbus; + optoe->chip = chip; + optoe->num_addresses = num_addresses; + strcpy(optoe->port_name, "unitialized"); + + /* + * Export the EEPROM bytes through sysfs, since that's convenient. + * By default, only root should see the data (maybe passwords etc) + */ + sysfs_bin_attr_init(&optoe->bin); + optoe->bin.attr.name = "eeprom"; + optoe->bin.attr.mode = S_IRUGO; + optoe->bin.read = optoe_bin_read; + optoe->bin.size = chip.byte_len; + + optoe->macc.read = optoe_macc_read; + + if (!use_smbus || + (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_WORD_DATA) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + /* + * NOTE: AN-2079 + * Finisar recommends that the host implement 1 byte writes + * only since this module only supports 32 byte page boundaries. + * 2 byte writes are acceptable for PE and Vout changes per + * Application Note AN-2071. + */ + unsigned write_max = 1; + + optoe->macc.write = optoe_macc_write; + + optoe->bin.write = optoe_bin_write; + optoe->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; + optoe->write_max = write_max; + + /* buffer (data + address at the beginning) */ + optoe->writebuf = kmalloc(write_max + 2, GFP_KERNEL); + if (!optoe->writebuf) { + err = -ENOMEM; + goto exit_kfree; + } + } else { + dev_warn(&client->dev, + "cannot write due to controller restrictions."); + } + + optoe->client[0] = client; + + /* use a dummy I2C device for two-address chips */ + for (i = 1; i < num_addresses; i++) { + optoe->client[i] = i2c_new_dummy(client->adapter, + client->addr + i); + if (!optoe->client[i]) { + dev_err(&client->dev, "address 0x%02x unavailable\n", + client->addr + i); + err = -EADDRINUSE; + goto err_struct; + } + } + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &optoe->bin); + if (err) + goto err_struct; + + optoe->attr_group = optoe_attr_group; + + err = sysfs_create_group(&client->dev.kobj, &optoe->attr_group); + if (err) { + dev_err(&client->dev, "failed to create sysfs attribute group.\n"); + goto err_struct; + } +#ifdef EEPROM_CLASS + optoe->eeprom_dev = eeprom_device_register(&client->dev, + chip.eeprom_data); + if (IS_ERR(optoe->eeprom_dev)) { + dev_err(&client->dev, "error registering eeprom device.\n"); + err = PTR_ERR(optoe->eeprom_dev); + goto err_sysfs_cleanup; + } +#endif + + i2c_set_clientdata(client, optoe); + + dev_info(&client->dev, "%zu byte %s EEPROM, %s\n", + optoe->bin.size, client->name, + optoe->bin.write ? "read/write" : "read-only"); + + if (use_smbus == I2C_SMBUS_WORD_DATA || + use_smbus == I2C_SMBUS_BYTE_DATA) { + dev_notice(&client->dev, "Falling back to %s reads, " + "performance will suffer\n", use_smbus == + I2C_SMBUS_WORD_DATA ? "word" : "byte"); + } + + if (chip.setup) + chip.setup(&optoe->macc, chip.context); + + return 0; + +#ifdef EEPROM_CLASS +err_sysfs_cleanup: + sysfs_remove_group(&client->dev.kobj, &optoe->attr_group); + sysfs_remove_bin_file(&client->dev.kobj, &optoe->bin); +#endif + +err_struct: + for (i = 1; i < num_addresses; i++) { + if (optoe->client[i]) + i2c_unregister_device(optoe->client[i]); + } + + kfree(optoe->writebuf); +exit_kfree: + kfree(optoe); +exit: + dev_dbg(&client->dev, "probe error %d\n", err); + + return err; +} + +/*-------------------------------------------------------------------------*/ + +static struct i2c_driver optoe_driver = { + .driver = { + .name = "optoe", + .owner = THIS_MODULE, + }, + .probe = optoe_probe, + .remove = optoe_remove, + .id_table = optoe_ids, +}; + +static int __init optoe_init(void) +{ + + if (!io_limit) { + pr_err("optoe: io_limit must not be 0!\n"); + return -EINVAL; + } + + io_limit = rounddown_pow_of_two(io_limit); + return i2c_add_driver(&optoe_driver); +} +module_init(optoe_init); + +static void __exit optoe_exit(void) +{ + i2c_del_driver(&optoe_driver); +} +module_exit(optoe_exit); + +MODULE_DESCRIPTION("Driver for optical transceiver (SFP, QSFP, ...) EEPROMs"); +MODULE_AUTHOR("DON BOLLINGER "); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/ym2651y.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/ym2651y.c new file mode 100755 index 000000000000..8e76c56b54fc --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/modules/ym2651y.c @@ -0,0 +1,603 @@ +/* + * An hwmon driver for the 3Y Power YM-2651Y Power Module + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_FAN_DUTY_CYCLE 100 + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x58, 0x5b, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct ym2651y_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 capability; /* Register value */ + u16 status_word; /* Register value */ + u8 fan_fault; /* Register value */ + u8 over_temp; /* Register value */ + u16 v_out; /* Register value */ + u16 i_out; /* Register value */ + u16 p_out; /* Register value */ + u16 temp; /* Register value */ + u16 fan_speed; /* Register value */ + u16 fan_duty_cycle[2]; /* Register value */ + u8 fan_dir[4]; /* Register value */ + u8 pmbus_revision; /* Register value */ + u8 mfr_id[10]; /* Register value */ + u8 mfr_model[10]; /* Register value */ + u8 mfr_revsion[3]; /* Register value */ + u16 mfr_vin_min; /* Register value */ + u16 mfr_vin_max; /* Register value */ + u16 mfr_iin_max; /* Register value */ + u16 mfr_iout_max; /* Register value */ + u16 mfr_pin_max; /* Register value */ + u16 mfr_pout_max; /* Register value */ + u16 mfr_vout_min; /* Register value */ + u16 mfr_vout_max; /* Register value */ +}; + +static ssize_t show_byte(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_word(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_linear(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_over_temp(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_ascii(struct device *dev, struct device_attribute *da, + char *buf); +static struct ym2651y_data *ym2651y_update_device(struct device *dev); +static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value); + +enum ym2651y_sysfs_attributes { + PSU_POWER_ON = 0, + PSU_TEMP_FAULT, + PSU_POWER_GOOD, + PSU_FAN1_FAULT, + PSU_FAN_DIRECTION, + PSU_OVER_TEMP, + PSU_V_OUT, + PSU_I_OUT, + PSU_P_OUT, + PSU_P_OUT_UV, /*In Unit of microVolt, instead of mini.*/ + PSU_TEMP1_INPUT, + PSU_FAN1_SPEED, + PSU_FAN1_DUTY_CYCLE, + PSU_PMBUS_REVISION, + PSU_MFR_ID, + PSU_MFR_MODEL, + PSU_MFR_REVISION, + PSU_MFR_VIN_MIN, + PSU_MFR_VIN_MAX, + PSU_MFR_VOUT_MIN, + PSU_MFR_VOUT_MAX, + PSU_MFR_IIN_MAX, + PSU_MFR_IOUT_MAX, + PSU_MFR_PIN_MAX, + PSU_MFR_POUT_MAX +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_power_on, S_IRUGO, show_word, NULL, PSU_POWER_ON); +static SENSOR_DEVICE_ATTR(psu_temp_fault, S_IRUGO, show_word, NULL, PSU_TEMP_FAULT); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_word, NULL, PSU_POWER_GOOD); +static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_fan_fault, NULL, PSU_FAN1_FAULT); +static SENSOR_DEVICE_ATTR(psu_over_temp, S_IRUGO, show_over_temp, NULL, PSU_OVER_TEMP); +static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, show_linear, NULL, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, show_linear, NULL, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, show_linear, NULL, PSU_P_OUT); +static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); +static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IWUSR | S_IRUGO, show_linear, set_fan_duty_cycle, PSU_FAN1_DUTY_CYCLE); +static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IRUGO, show_ascii, NULL, PSU_FAN_DIRECTION); +static SENSOR_DEVICE_ATTR(psu_pmbus_revision, S_IRUGO, show_byte, NULL, PSU_PMBUS_REVISION); +static SENSOR_DEVICE_ATTR(psu_mfr_id, S_IRUGO, show_ascii, NULL, PSU_MFR_ID); +static SENSOR_DEVICE_ATTR(psu_mfr_model, S_IRUGO, show_ascii, NULL, PSU_MFR_MODEL); +static SENSOR_DEVICE_ATTR(psu_mfr_revision, S_IRUGO, show_ascii, NULL, PSU_MFR_REVISION); +static SENSOR_DEVICE_ATTR(psu_mfr_vin_min, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MIN); +static SENSOR_DEVICE_ATTR(psu_mfr_vin_max, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_vout_min, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MIN); +static SENSOR_DEVICE_ATTR(psu_mfr_vout_max, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_iin_max, S_IRUGO, show_linear, NULL, PSU_MFR_IIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_iout_max, S_IRUGO, show_linear, NULL, PSU_MFR_IOUT_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_pin_max, S_IRUGO, show_linear, NULL, PSU_MFR_PIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_pout_max, S_IRUGO, show_linear, NULL, PSU_MFR_POUT_MAX); + +/*Duplicate nodes for lm-sensors.*/ +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, show_linear, NULL, PSU_P_OUT_UV); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); +static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_word, NULL, PSU_TEMP_FAULT); + +static struct attribute *ym2651y_attributes[] = { + &sensor_dev_attr_psu_power_on.dev_attr.attr, + &sensor_dev_attr_psu_temp_fault.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, + &sensor_dev_attr_psu_over_temp.dev_attr.attr, + &sensor_dev_attr_psu_v_out.dev_attr.attr, + &sensor_dev_attr_psu_i_out.dev_attr.attr, + &sensor_dev_attr_psu_p_out.dev_attr.attr, + &sensor_dev_attr_psu_temp1_input.dev_attr.attr, + &sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr, + &sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr, + &sensor_dev_attr_psu_fan_dir.dev_attr.attr, + &sensor_dev_attr_psu_pmbus_revision.dev_attr.attr, + &sensor_dev_attr_psu_mfr_id.dev_attr.attr, + &sensor_dev_attr_psu_mfr_model.dev_attr.attr, + &sensor_dev_attr_psu_mfr_revision.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vin_min.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_pout_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_iin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_pin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vout_min.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vout_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_iout_max.dev_attr.attr, + /*Duplicate nodes for lm-sensors.*/ + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + NULL +}; + +static ssize_t show_byte(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + + return (attr->index == PSU_PMBUS_REVISION) ? sprintf(buf, "%d\n", data->pmbus_revision) : + sprintf(buf, "0\n"); +} + +static ssize_t show_word(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + u16 status = 0; + + switch (attr->index) { + case PSU_POWER_ON: /* psu_power_on, low byte bit 6 of status_word, 0=>ON, 1=>OFF */ + status = (data->status_word & 0x40) ? 0 : 1; + break; + case PSU_TEMP_FAULT: /* psu_temp_fault, low byte bit 2 of status_word, 0=>Normal, 1=>temp fault */ + status = (data->status_word & 0x4) >> 2; + break; + case PSU_POWER_GOOD: /* psu_power_good, high byte bit 3 of status_word, 0=>OK, 1=>FAIL */ + status = (data->status_word & 0x800) ? 0 : 1; + break; + } + + return sprintf(buf, "%d\n", status); +} + +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(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct ym2651y_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; + + mutex_lock(&data->update_lock); + data->fan_duty_cycle[nr] = speed; + ym2651y_write_word(client, 0x3B + nr, data->fan_duty_cycle[nr]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_linear(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + + u16 value = 0; + int exponent, mantissa; + int multiplier = 1000; + + switch (attr->index) { + case PSU_V_OUT: + value = data->v_out; + break; + case PSU_I_OUT: + value = data->i_out; + break; + case PSU_P_OUT_UV: + multiplier = 1000000; /*For lm-sensors, unit is micro-Volt.*/ + /*Passing through*/ + case PSU_P_OUT: + value = data->p_out; + break; + case PSU_TEMP1_INPUT: + value = data->temp; + break; + case PSU_FAN1_SPEED: + value = data->fan_speed; + multiplier = 1; + break; + case PSU_FAN1_DUTY_CYCLE: + value = data->fan_duty_cycle[0]; + multiplier = 1; + break; + case PSU_MFR_VIN_MIN: + value = data->mfr_vin_min; + break; + case PSU_MFR_VIN_MAX: + value = data->mfr_vin_max; + break; + case PSU_MFR_VOUT_MIN: + value = data->mfr_vout_min; + break; + case PSU_MFR_VOUT_MAX: + value = data->mfr_vout_max; + break; + case PSU_MFR_PIN_MAX: + value = data->mfr_pin_max; + break; + case PSU_MFR_POUT_MAX: + value = data->mfr_pout_max; + break; + case PSU_MFR_IOUT_MAX: + value = data->mfr_iout_max; + break; + case PSU_MFR_IIN_MAX: + value = data->mfr_iin_max; + 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 show_fan_fault(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + + u8 shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6; + + return sprintf(buf, "%d\n", data->fan_fault >> shift); +} + +static ssize_t show_over_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct ym2651y_data *data = ym2651y_update_device(dev); + + return sprintf(buf, "%d\n", data->over_temp >> 7); +} + +static ssize_t show_ascii(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + u8 *ptr = NULL; + + switch (attr->index) { + case PSU_FAN_DIRECTION: /* psu_fan_dir */ + ptr = data->fan_dir; + break; + case PSU_MFR_ID: /* psu_mfr_id */ + ptr = data->mfr_id; + break; + case PSU_MFR_MODEL: /* psu_mfr_model */ + ptr = data->mfr_model; + break; + case PSU_MFR_REVISION: /* psu_mfr_revision */ + ptr = data->mfr_revsion; + break; + default: + return 0; + } + + return sprintf(buf, "%s\n", ptr); +} + +static const struct attribute_group ym2651y_group = { + .attrs = ym2651y_attributes, +}; + +static int ym2651y_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct ym2651y_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct ym2651y_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &ym2651y_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &ym2651y_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int ym2651y_remove(struct i2c_client *client) +{ + struct ym2651y_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &ym2651y_group); + kfree(data); + + return 0; +} + +static const struct i2c_device_id ym2651y_id[] = { + { "ym2651", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ym2651y_id); + +static struct i2c_driver ym2651y_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ym2651", + }, + .probe = ym2651y_probe, + .remove = ym2651y_remove, + .id_table = ym2651y_id, + .address_list = normal_i2c, +}; + +static int ym2651y_read_byte(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int ym2651y_read_word(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_data(client, reg); +} + +static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_word_data(client, reg, value); +} + +static int ym2651y_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 ym2651y_data *ym2651y_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ym2651y_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int i, status; + u8 command; + u8 fan_dir[5] = {0}; + struct reg_data_byte regs_byte[] = { {0x19, &data->capability}, + {0x7d, &data->over_temp}, + {0x81, &data->fan_fault}, + {0x98, &data->pmbus_revision}}; + struct reg_data_word regs_word[] = { {0x79, &data->status_word}, + {0x8b, &data->v_out}, + {0x8c, &data->i_out}, + {0x96, &data->p_out}, + {0x8d, &data->temp}, + {0x3b, &(data->fan_duty_cycle[0])}, + {0x3c, &(data->fan_duty_cycle[1])}, + {0x90, &data->fan_speed}, + {0xa0, &data->mfr_vin_min}, + {0xa1, &data->mfr_vin_max}, + {0xa2, &data->mfr_iin_max}, + {0xa3, &data->mfr_pin_max}, + {0xa4, &data->mfr_vout_min}, + {0xa5, &data->mfr_vout_max}, + {0xa6, &data->mfr_iout_max}, + {0xa7, &data->mfr_pout_max}}; + + dev_dbg(&client->dev, "Starting ym2651 update\n"); + + /* Read byte data */ + for (i = 0; i < ARRAY_SIZE(regs_byte); i++) { + status = ym2651y_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; + } + } + + /* Read word data */ + for (i = 0; i < ARRAY_SIZE(regs_word); i++) { + status = ym2651y_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; + } + } + + /* Read fan_direction */ + command = 0xC3; + status = ym2651y_read_block(client, command, fan_dir, ARRAY_SIZE(fan_dir)-1); + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + } + + strncpy(data->fan_dir, fan_dir+1, ARRAY_SIZE(data->fan_dir)-1); + data->fan_dir[ARRAY_SIZE(data->fan_dir)-1] = '\0'; + + /* Read mfr_id */ + command = 0x99; + status = ym2651y_read_block(client, command, data->mfr_id, + ARRAY_SIZE(data->mfr_id)-1); + data->mfr_id[ARRAY_SIZE(data->mfr_id)-1] = '\0'; + + if (status < 0) + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + + /* Read mfr_model */ + command = 0x9a; + status = ym2651y_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); + + /* Read mfr_revsion */ + command = 0x9b; + status = ym2651y_read_block(client, command, data->mfr_revsion, + ARRAY_SIZE(data->mfr_revsion)-1); + data->mfr_revsion[ARRAY_SIZE(data->mfr_revsion)-1] = '\0'; + + if (status < 0) + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +module_i2c_driver(ym2651y_driver); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("3Y Power YM-2651Y driver"); +MODULE_LICENSE("GPL"); + + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/service/as7716-platform-monitor.service b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/service/as7716-platform-monitor.service new file mode 100755 index 000000000000..b1428d7844f8 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/service/as7716-platform-monitor.service @@ -0,0 +1,17 @@ +[Unit] +Description=Accton AS7716-32X Platform Monitoring service +Before=pmon.service +After=sysinit.target +DefaultDependencies=no + +[Service] +ExecStartPre=/usr/local/bin/accton_as7716_util.py install +ExecStart=/usr/local/bin/accton_as7716_monitor.py +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/setup.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/setup.py new file mode 100755 index 000000000000..7a3d784d326e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/setup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +import os +import sys +from setuptools import setup +os.listdir + +setup( + name='as7716_32x', + version='1.0', + description='Module to initialize Accton AS7716-32X platforms', + + packages=['as7716_32x'], + package_dir={'as7716_32x': 'as7716-32x/classes'}, +) + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/utils/README b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/utils/README new file mode 100755 index 000000000000..44e03cab5f52 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/utils/README @@ -0,0 +1,117 @@ +Copyright (C) 2016 Accton Networks, Inc. + +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 3 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, see . + +Contents of this package: + patch - files under patch/ is for kernel and ONIE installer + for the kernel: + config-accton-as5712_54x.patch + for kernel configuration. + driver-i2c-muxes-pca954x-always-deselect.patch + for i2c_mux deselects after transaction. + driver-patches-for-accton-as5712-fan-psu-cpld.patch + for as5712's fan/psu/cpld/led/sfp drivers. + for ONIE: + onie_installer-accton-AS5712-54X.patch + for console port setting and copy util script o rootfs. + module - Contains source code of as5712 kernel driver modules. + +The late Sonic building scripts, pushed @Dec 5 2016, will automatically +create a docker container and run building process under it. +User is not necessary to handle docker environment creation. + +1. Download sonic-buildimage environment. + - Run "git clone https://github.com/Azure/sonic-buildimage". + - cd to sonic-buildimage and run "git submodule update --init --recursive". +2. Build kernel + - cd ./src/sonic-linux-kernel + - Copy patches and series from patch/kernel of this release to + sonic-linux-kernel/patch. + - Build kernel by "make". + - The built kernel package, linux-image-3.16.0-5-amd64_3.16.51-3+deb8u1_amd64.deb + , is generated. +3. Build installer + - Change directory back to sonic-buildimage/. + - Get onie_installer-accton-AS5712-54X.patch" from patch/installer. + - Change setting for AS5712-54X by patching build_image.sh. + "patch -p1 < onie_installer-accton-AS5712-54X.patch" + !!NOTICE, patching onie_installer-accton-AS5712-54X.patch comments out the + "git status" checking at build_image.sh. + - The account and password of installed OS can be given at rules/config. + The default user and password are "admin" & "YourPaSsWoRd" respectively. + - Run "make configure PLATFORM=broadcom" + - Copy the built kernel debian package to target/debs/. + The file is linux-image-3.16.0-5-amd64_*_amd64.deb under directory + src/sonic-linux-kernel/. + - Run "make target/sonic-generic.bin" + - Get the installer, target/sonic-generic.bin, to target machine and install. + +All Linux kernel code is licensed under the GPLv1. All other code is +licensed under the GPLv3. Please see the LICENSE file for copies of +both licenses. + +The code for integacting with Accton AS5712-54X has 2 parts, +kernel drivers and operational script. +The kernel drivers of peripherals are under module/ directory. +1. These drivers are patched into kernel by + driver-patches-for-accton-as5712-fan-psu-cpld.patch + Or you can build the driver under module/ by setting environment variable, + KERNEL_SRC, to proper linux built directory and run make. + It may be sonic-linux-kernel/linux-3.*/debian/build/build_amd64_none_amd64/. +2. A operational script, accton_as5712_util.py, for device initializatian and + peripheral accessing should be installed at /usr/bin. + This script is generated by onie_installer-accton-AS5712-54X.patch. + It's done by patching onie_installer-accton-AS5712-54X.patch at build-image. + Run "accton_as5712_util.py install" to install drivers. + +To initialize the system, run "accton_as5712_util.py install". +To clean up the drivers & devices, run "accton_as5712_util.py clean". +To dump information of sensors, run "accton_as5712_util.py show". +To dump SFP EEPROM, run "accton_as5712_util.py sff". +To set fan speed, run "accton_as5712_util.py set fan". +To enable/disable SFP emission, run "accton_as5712_util.py set sfp". +To set system LEDs' color, run "accton_as5712_util.py set led" +For more information, run "accton_as5712_util.py --help". + +==================================================================== +Besides applying accton_as5712_util.py to access peripherals, you can +access peripherals by sysfs nodes directly after the installation is run. + +System LED: + There are 5 system LEDs at the lower-left corner of front panel. + They are loc, diag, fan, ps1, and ps2. + The sysfs interface color mappings are as follows: + Brightness: + 0 => off + 1 => green + 2 => amber + 3 => red + 4 => blue + But not all colors are available for each LED. + +Fan Control: + There are 10 fans inside 5 fan modules. + All fans share 1 duty setting, ranged from 0~100. + +Thermal sensers: + 3 temperature sensors are controlled by the lm75 kernel modules. + +PSUs: + There 2 power supplies slot at the left/right side of the back. + Once if a PSU is not plugged, the status of it is shown failed. + +There are 48 SFP+ and 6 QSFP modules are equipped. +Before operating on PSU and QSFP+, please make sure it is well plugged. +Otherwise, operation is going to fail. + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/utils/accton_as7716_monitor.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/utils/accton_as7716_monitor.py new file mode 100755 index 000000000000..63f26d5d2ef6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/utils/accton_as7716_monitor.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# 1/10/2018: Jostar modify for as7716_32 +# ------------------------------------------------------------------ + +try: + import os + import sys, getopt + import subprocess + import click + import imp + import logging + import logging.config + import types + import time # this is only being used as part of the example + import traceback + from tabulate import tabulate + from as7716_32x.fanutil import FanUtil + from as7716_32x.thermalutil import ThermalUtil +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + +# Deafults +VERSION = '1.0' +FUNCTION_NAME = 'accton_as7716_monitor' + +global log_file +global log_level + + # For AC power Front to Back : + # If any fan fail, please fan speed register to 15 + # The max value of Fan speed register is 9 + # [LM75(48) + LM75(49) + LM75(4A)] > 174 => set Fan speed value from 4 to 5 + # [LM75(48) + LM75(49) + LM75(4A)] > 182 => set Fan speed value from 5 to 7 + # [LM75(48) + LM75(49) + LM75(4A)] > 190 => set Fan speed value from 7 to 9 + # + # [LM75(48) + LM75(49) + LM75(4A)] < 170 => set Fan speed value from 5 to 4 + # [LM75(48) + LM75(49) + LM75(4A)] < 178 => set Fan speed value from 7 to 5 + # [LM75(48) + LM75(49) + LM75(4A)] < 186 => set Fan speed value from 9 to 7 + # + # + # For AC power Back to Front : + # If any fan fail, please fan speed register to 15 + # The max value of Fan speed register is 10 + # [LM75(48) + LM75(49) + LM75(4A)] > 140 => set Fan speed value from 4 to 5 + # [LM75(48) + LM75(49) + LM75(4A)] > 150 => set Fan speed value from 5 to 7 + # [LM75(48) + LM75(49) + LM75(4A)] > 160 => set Fan speed value from 7 to 10 + # + # [LM75(48) + LM75(49) + LM75(4A)] < 135 => set Fan speed value from 5 to 4 + # [LM75(48) + LM75(49) + LM75(4A)] < 145 => set Fan speed value from 7 to 5 + # [LM75(48) + LM75(49) + LM75(4A)] < 155 => set Fan speed value from 10 to 7 + # + + + # 2.If no matched fan speed is found from the policy, + # use FAN_DUTY_CYCLE_MIN as default speed + # Get current temperature + # 4.Decision 3: Decide new fan speed depend on fan direction/current fan speed/temperature + + + + +# Make a class we can use to capture stdout and sterr in the log +class accton_as7716_monitor(object): + # static temp var + _ori_temp = 0 + _new_perc = 0 + _ori_perc = 0 + + def __init__(self, log_file, log_level): + """Needs a logger and a logger level.""" + # set up logging to file + logging.basicConfig( + filename=log_file, + filemode='w', + level=log_level, + format= '[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s', + datefmt='%H:%M:%S' + ) + + # set up logging to console + if log_level == logging.DEBUG: + console = logging.StreamHandler() + console.setLevel(log_level) + formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + + logging.debug('SET. logfile:%s / loglevel:%d', log_file, log_level) + + def manage_fans(self): + + fan_policy_f2b = { + 0: [32, 0, 174000], + 1: [38, 170000, 182000], + 2: [50, 178000, 190000], + 3: [63, 186000, 0], + } + fan_policy_b2f = { + 0: [32, 0, 140000], + 1: [38, 135000, 150000], + 2: [50, 145000, 160000], + 3: [69, 15500, 0], + } + + thermal = ThermalUtil() + fan = FanUtil() + get_temp = thermal.get_thermal_temp() + + cur_duty_cycle = fan.get_fan_duty_cycle() + + for x in range(fan.get_idx_fan_start(), fan.get_num_fans()+1): + fan_status = fan.get_fan_status(x) + if fan_status is None: + logging.debug('INFO. SET new_perc to %d (FAN stauts is None. fan_num:%d)', 100, x) + return False + if fan_status is False: + logging.debug('INFO. SET new_perc to %d (FAN fault. fan_num:%d)', 100, x) + fan.set_fan_duty_cycle(45) + return True + logging.debug('INFO. fan_status is True (fan_num:%d)', x) + + if fan_status is not None and fan_status is not False: + fan_dir=fan.get_fan_dir(1) + for x in range(0, 4): + if cur_duty_cycle == fan_policy_f2b[x][0]: + break + + if fan_dir == 1: + if x == 4 : + fan.set_fan_duty_cycle(fan_policy_f2b[0][0]) + new_duty_cycle=cur_duty_cycle + # if temp > up_levle, else if temp < down_level + if get_temp > fan_policy_f2b[x][2] and x != 3 : + new_duty_cycle= fan_policy_f2b[x+1][0] + logging.debug('INFO. THERMAL temp UP, temp %d > %d , new_duty_cycle=%d', get_temp, fan_policy_f2b[x][2], new_duty_cycle) + elif get_temp < fan_policy_f2b[x][1] : + new_duty_cycle= fan_policy_f2b[x-1][0] + logging.debug('INFO. THERMAL temp down, temp %d < %d , new_duty_cycle=%d', get_temp, fan_policy_f2b[x][1], new_duty_cycle) + if new_duty_cycle == cur_duty_cycle : + return True + else: + if x == 4 : + fan.set_fan_duty_cycle(fan_policy_b2f[0][0]) + new_duty_cycle=cur_duty_cycle + # if temp > up_levle, else if temp < down_level + if get_temp > fan_policy_b2f[x][1] and x != 3 : + new_duty_cycle= fan_policy_b2f[x+1][0] + logging.debug('INFO. THERMAL temp UP, temp %d > %d , new_duty_cycle=%d', get_temp, fan_policy_b2f[x][2], new_duty_cycle) + elif get_temp < fan_policy_b2f[x][0] and x != 0 : + new_duty_cycle= fan_policy_b2f[x-1][0] + logging.debug('INFO. THERMAL temp down, temp %d < %d , new_duty_cycle=%d', get_temp, fan_policy_b2f[x][1], new_duty_cycle) + if new_duty_cycle == cur_duty_cycle : + return True + + fan.set_fan_duty_cycle(new_duty_cycle) + + return True + + + +def main(argv): + log_file = '%s.log' % FUNCTION_NAME + log_level = logging.INFO + if len(sys.argv) != 1: + try: + opts, args = getopt.getopt(argv,'hdl:',['lfile=']) + except getopt.GetoptError: + print 'Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + for opt, arg in opts: + if opt == '-h': + print 'Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + elif opt in ('-d', '--debug'): + log_level = logging.DEBUG + elif opt in ('-l', '--lfile'): + log_file = arg + + monitor = accton_as7716_monitor(log_file, log_level) + + # Loop forever, doing something useful hopefully: + while True: + monitor.manage_fans() + time.sleep(1) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32x/utils/accton_as7716_util.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/utils/accton_as7716_util.py new file mode 100755 index 000000000000..cd35b50d200e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32x/utils/accton_as7716_util.py @@ -0,0 +1,595 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 Accton Networks, Inc. +# +# 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 3 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, see . + +""" +Usage: %(scriptName)s [options] command object + +options: + -h | --help : this help message + -d | --debug : run with debug mode + -f | --force : ignore error during installation or clean +command: + install : install drivers and generate related sysfs nodes + clean : uninstall drivers and remove related sysfs nodes + show : show all systen status + sff : dump SFP eeprom + set : change board setting with fan|led|sfp +""" + +import os +import commands +import sys, getopt +import logging +import re +import time +from collections import namedtuple + +PROJECT_NAME = 'as7716_32x' +version = '0.0.1' +verbose = False +DEBUG = False +args = [] +ALL_DEVICE = {} +DEVICE_NO = {'led':5, 'fan1':1, 'fan2':1,'fan3':1,'fan4':1,'fan5':1,'thermal':3, 'psu':2, 'sfp':54} + + +led_prefix ='/sys/devices/platform/as7716_32x_led/leds/accton_'+PROJECT_NAME+'_led::' +fan_prefix ='/sys/devices/platform/as7716_32x_' +hwmon_types = {'led': ['diag','fan','loc','psu1','psu2'], + 'fan1': ['fan'], + 'fan2': ['fan'], + 'fan3': ['fan'], + 'fan4': ['fan'], + 'fan5': ['fan'], + 'fan5': ['fan'], + } +hwmon_nodes = {'led': ['brightness'] , + 'fan1': ['fan_duty_cycle_percentage', 'fan1_fault', 'fan1_speed_rpm', 'fan1_direction', 'fanr1_fault', 'fanr1_speed_rpm'], + 'fan2': ['fan_duty_cycle_percentage','fan2_fault', 'fan2_speed_rpm', 'fan2_direction', 'fanr2_fault', 'fanr2_speed_rpm'], + 'fan3': ['fan_duty_cycle_percentage','fan3_fault', 'fan3_speed_rpm', 'fan3_direction', 'fanr3_fault', 'fanr3_speed_rpm'], + 'fan4': ['fan4_duty_cycle_percentage','fan4_fault', 'fan4_speed_rpm', 'fan4_direction', 'fanr4_fault', 'fanr4_speed_rpm'], + 'fan5': ['fan_duty_cycle_percentage','fan5_fault', 'fan5_speed_rpm', 'fan5_direction', 'fanr5_fault', 'fanr5_speed_rpm'], + } +hwmon_prefix ={'led': led_prefix, + 'fan1': fan_prefix, + 'fan2': fan_prefix, + 'fan3': fan_prefix, + 'fan4': fan_prefix, + 'fan5': fan_prefix, + } + +i2c_prefix = '/sys/bus/i2c/devices/' +i2c_bus = {'thermal': ['10-0048','10-0049', '10-004a'] , + 'psu': ['17-0050','18-0053'], + 'sfp': ['-0050']} +i2c_nodes = { + 'thermal': ['hwmon/hwmon*/temp1_input'] , + 'psu': ['psu_present ', 'psu_power_good'] , + 'sfp': ['sfp_is_present ', 'sfp_tx_disable']} + +sfp_map = [29, 30, 31, 32, 34, 33, 36, 35, + 25, 26, 27, 28, 37, 38, 39, 40, + 41, 42, 43, 44, 53, 54, 55, 56, + 45, 46, 47, 48, 49, 50, 51, 52] + +mknod =[ +'echo pca9548 0x77 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo pca9548 0x76 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo as7716_32x_fan 0x66 > /sys/bus/i2c/devices/i2c-9/new_device', + +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-10/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-10/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-10/new_device', + +'echo as7716_32x_cpld1 0x60 > /sys/bus/i2c/devices/i2c-11/new_device', +'echo accton_i2c_cpld 0x62 > /sys/bus/i2c/devices/i2c-12/new_device', +'echo accton_i2c_cpld 0x64 > /sys/bus/i2c/devices/i2c-13/new_device', + +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-2/new_device', +'echo pca9548 0x72 > /sys/bus/i2c/devices/i2c-2/new_device', +'echo pca9548 0x73 > /sys/bus/i2c/devices/i2c-2/new_device', +'echo pca9548 0x74 > /sys/bus/i2c/devices/i2c-2/new_device', +'echo pca9548 0x75 > /sys/bus/i2c/devices/i2c-2/new_device', + +# PSU-1 +'echo as7716_32x_psu1 0x53 > /sys/bus/i2c/devices/i2c-18/new_device', +'echo ym2651 0x5b > /sys/bus/i2c/devices/i2c-18/new_device', + +# PSU-2 +'echo as7716_32x_psu2 0x50> /sys/bus/i2c/devices/i2c-17/new_device', +'echo ym2651 0x58 > /sys/bus/i2c/devices/i2c-17/new_device', + + + +#EERPOM +'echo 24c02 0x56 > /sys/bus/i2c/devices/i2c-1/new_device', +] + +mknod2 =[ +'echo as7716_32x_cpld1 0x60 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo accton_i2c_cpld 0x61 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo accton_i2c_cpld 0x62 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-0/new_device', + +# PSU-1 +'echo as7716_32x_psu1 0x38 > /sys/bus/i2c/devices/i2c-57/new_device', +'echo cpr_4011_4mxx 0x3c > /sys/bus/i2c/devices/i2c-57/new_device', +'echo as7716_32x_psu1 0x50 > /sys/bus/i2c/devices/i2c-57/new_device', +'echo ym2401 0x58 > /sys/bus/i2c/devices/i2c-57/new_device', + +# PSU-2 +'echo as7716_32x_psu2 0x3b > /sys/bus/i2c/devices/i2c-58/new_device', +'echo cpr_4011_4mxx 0x3f > /sys/bus/i2c/devices/i2c-58/new_device', +'echo as7716_32x_psu2 0x53 > /sys/bus/i2c/devices/i2c-58/new_device', +'echo ym2401 0x5b > /sys/bus/i2c/devices/i2c-58/new_device', + +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-61/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-62/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-63/new_device', + +#EERPOM +'echo 24c02 0x57 > /sys/bus/i2c/devices/i2c-1/new_device', +] + +FORCE = 0 +logging.basicConfig(filename= PROJECT_NAME+'.log', filemode='w',level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) + + +if DEBUG == True: + print sys.argv[0] + print 'ARGV :', sys.argv[1:] + + +def main(): + global DEBUG + global args + global FORCE + + if len(sys.argv)<2: + show_help() + + options, args = getopt.getopt(sys.argv[1:], 'hdf', ['help', + 'debug', + 'force', + ]) + if DEBUG == True: + print options + print args + print len(sys.argv) + + for opt, arg in options: + if opt in ('-h', '--help'): + show_help() + elif opt in ('-d', '--debug'): + DEBUG = True + logging.basicConfig(level=logging.INFO) + elif opt in ('-f', '--force'): + FORCE = 1 + else: + logging.info('no option') + for arg in args: + if arg == 'install': + do_install() + elif arg == 'clean': + do_uninstall() + elif arg == 'show': + device_traversal() + elif arg == 'sff': + if len(args)!=2: + show_eeprom_help() + elif int(args[1]) ==0 or int(args[1]) > DEVICE_NO['sfp']: + show_eeprom_help() + else: + show_eeprom(args[1]) + return + elif arg == 'set': + if len(args)<3: + show_set_help() + else: + set_device(args[1:]) + return + else: + show_help() + + + return 0 + +def show_help(): + print __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]} + sys.exit(0) + +def show_set_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print cmd +" [led|sfp|fan]" + print " use \""+ cmd + " led 0-4 \" to set led color" + print " use \""+ cmd + " fan 0-100\" to set fan duty percetage" + print " use \""+ cmd + " sfp 1-32 {0|1}\" to set sfp# tx_disable" + sys.exit(0) + +def show_eeprom_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print " use \""+ cmd + " 1-32 \" to dump sfp# eeprom" + sys.exit(0) + +def my_log(txt): + if DEBUG == True: + print "[ACCTON DBG]: "+txt + return + +def log_os_system(cmd, show): + logging.info('Run :'+cmd) + status = 1 + output = "" + status, output = commands.getstatusoutput(cmd) + my_log (cmd +"with result:" + str(status)) + my_log ("cmd:" + cmd) + my_log (" output:"+output) + if status: + logging.info('Failed :'+cmd) + if show: + print('Failed :'+cmd) + return status, output + +def driver_inserted(): + ret, lsmod = log_os_system("lsmod| grep accton", 0) + logging.info('mods:'+lsmod) + if len(lsmod) ==0: + return False + +#'modprobe cpr_4011_4mxx', + +kos = [ +'depmod -ae', +'modprobe i2c_dev', +'modprobe i2c_mux_pca954x', +'modprobe accton_i2c_cpld', +'modprobe cpr_4011_4mxx', +'modprobe ym2651y', +'modprobe accton_as7716_32x_cpld1', +'modprobe accton_as7716_32x_fan', +'modprobe accton_as7716_32x_leds', +'modprobe accton_as7716_32x_psu'] + +def driver_install(): + global FORCE + for i in range(0,len(kos)): + status, output = log_os_system(kos[i], 1) + if status: + if FORCE == 0: + return status + return 0 + +def driver_uninstall(): + global FORCE + for i in range(0,len(kos)): + rm = kos[-(i+1)].replace("modprobe", "modprobe -rq") + rm = rm.replace("insmod", "rmmod") + status, output = log_os_system(rm, 1) + if status: + if FORCE == 0: + return status + return 0 + + + +def i2c_order_check(): + # i2c bus 0 and 1 might be installed in different order. + # Here check if 0x76 is exist @ i2c-0 + tmp = "echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-1/new_device" + status, output = log_os_system(tmp, 0) + if not device_exist(): + order = 1 + else: + order = 0 + tmp = "echo 0x70 > /sys/bus/i2c/devices/i2c-1/delete_device" + status, output = log_os_system(tmp, 0) + return order + +def device_install(): + global FORCE + + for i in range(0,len(mknod)): + #for pca932x need times to built new i2c buses + if mknod[i].find('pca954') != -1: + time.sleep(2) + + status, output = log_os_system(mknod[i], 1) + if status: + print output + if FORCE == 0: + return status + + for i in range(0,len(sfp_map)): + status, output =log_os_system("echo optoe1 0x50 > /sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/new_device", 1) + if status: + print output + if FORCE == 0: + return status + status, output =log_os_system("echo port"+str(i)+" > /sys/bus/i2c/devices/"+str(sfp_map[i])+"-0050/port_name", 1) + if status: + print output + if FORCE == 0: + return status + return + +def device_uninstall(): + global FORCE + + status, output =log_os_system("ls /sys/bus/i2c/devices/0-0070", 0) + if status==0: + I2C_ORDER=1 + else: + I2C_ORDER=0 + + for i in range(0,len(sfp_map)): + target = "/sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/delete_device" + status, output =log_os_system("echo 0x50 > "+ target, 1) + if status: + print output + if FORCE == 0: + return status + + if I2C_ORDER==0: + nodelist = mknod + else: + nodelist = mknod2 + + for i in range(len(nodelist)): + target = nodelist[-(i+1)] + temp = target.split() + del temp[1] + temp[-1] = temp[-1].replace('new_device', 'delete_device') + status, output = log_os_system(" ".join(temp), 1) + if status: + print output + if FORCE == 0: + return status + + return + +def system_ready(): + if driver_inserted() == False: + return False + if not device_exist(): + print "not device_exist()" + return False + return True + +def do_install(): + if driver_inserted() == False: + status = driver_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" drivers detected...." + if not device_exist(): + status = device_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" devices detected...." + return + +def do_uninstall(): + if not device_exist(): + print PROJECT_NAME.upper() +" has no device installed...." + else: + print "Removing device...." + status = device_uninstall() + if status: + if FORCE == 0: + return status + + if driver_inserted()== False : + print PROJECT_NAME.upper() +" has no driver installed...." + else: + print "Removing installed driver...." + status = driver_uninstall() + if status: + if FORCE == 0: + return status + + return + +def devices_info(): + global DEVICE_NO + global ALL_DEVICE + global i2c_bus, hwmon_types + for key in DEVICE_NO: + ALL_DEVICE[key]= {} + for i in range(0,DEVICE_NO[key]): + ALL_DEVICE[key][key+str(i+1)] = [] + + for key in i2c_bus: + buses = i2c_bus[key] + nodes = i2c_nodes[key] + for i in range(0,len(buses)): + for j in range(0,len(nodes)): + if 'fan' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ buses[i]+"/fan"+str(k+1)+"_"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + elif 'sfp' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ str(sfp_map[k])+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + else: + node = key+str(i+1) + path = i2c_prefix+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + + for key in hwmon_types: + itypes = hwmon_types[key] + nodes = hwmon_nodes[key] + for i in range(0,len(itypes)): + for j in range(0,len(nodes)): + node = key+"_"+itypes[i] + path = hwmon_prefix[key]+ itypes[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][ key+str(i+1)].append(path) + + #show dict all in the order + if DEBUG == True: + for i in sorted(ALL_DEVICE.keys()): + print(i+": ") + for j in sorted(ALL_DEVICE[i].keys()): + print(" "+j) + for k in (ALL_DEVICE[i][j]): + print(" "+" "+k) + return + +def show_eeprom(index): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + node = ALL_DEVICE['sfp'] ['sfp'+str(index)][0] + node = node.replace(node.split("/")[-1], 'sfp_eeprom') + # check if got hexdump command in current environment + ret, log = log_os_system("which hexdump", 0) + ret, log2 = log_os_system("which busybox hexdump", 0) + if len(log): + hex_cmd = 'hexdump' + elif len(log2): + hex_cmd = ' busybox hexdump' + else: + log = 'Failed : no hexdump cmd!!' + logging.info(log) + print log + return 1 + + print node + ":" + ret, log = log_os_system("cat "+node+"| "+hex_cmd+" -C", 1) + if ret==0: + print log + else: + print "**********device no found**********" + return + +def set_device(args): + global DEVICE_NO + global ALL_DEVICE + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + + if args[0]=='led': + if int(args[1])>4: + show_set_help() + return + #print ALL_DEVICE['led'] + for i in range(0,len(ALL_DEVICE['led'])): + for k in (ALL_DEVICE['led']['led'+str(i+1)]): + ret, log = log_os_system("echo "+args[1]+" >"+k, 1) + if ret: + return ret + elif args[0]=='fan': + if int(args[1])>100: + show_set_help() + return + #print ALL_DEVICE['fan'] + #fan1~6 is all fine, all fan share same setting + node = ALL_DEVICE['fan1'] ['fan11'][0] + node = node.replace(node.split("/")[-1], 'fan1_duty_cycle_percentage') + ret, log = log_os_system("cat "+ node, 1) + if ret==0: + print ("Previous fan duty: " + log.strip() +"%") + ret, log = log_os_system("echo "+args[1]+" >"+node, 1) + if ret==0: + print ("Current fan duty: " + args[1] +"%") + return ret + elif args[0]=='sfp': + if int(args[1])> DEVICE_NO[args[0]] or int(args[1])==0: + show_set_help() + return + if len(args)<2: + show_set_help() + return + + if int(args[2])>1: + show_set_help() + return + + #print ALL_DEVICE[args[0]] + for i in range(0,len(ALL_DEVICE[args[0]])): + for j in ALL_DEVICE[args[0]][args[0]+str(args[1])]: + if j.find('tx_disable')!= -1: + ret, log = log_os_system("echo "+args[2]+" >"+ j, 1) + if ret: + return ret + + return + +#get digits inside a string. +#Ex: 31 for "sfp31" +def get_value(input): + digit = re.findall('\d+', input) + return int(digit[0]) + +def device_traversal(): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + for i in sorted(ALL_DEVICE.keys()): + print("============================================") + print(i.upper()+": ") + print("============================================") + + for j in sorted(ALL_DEVICE[i].keys(), key=get_value): + print " "+j+":", + for k in (ALL_DEVICE[i][j]): + ret, log = log_os_system("cat "+k, 0) + func = k.split("/")[-1].strip() + func = re.sub(j+'_','',func,1) + func = re.sub(i.lower()+'_','',func,1) + if ret==0: + print func+"="+log+" ", + else: + print func+"="+"X"+" ", + print + print("----------------------------------------------------------------") + + + print + return + +def device_exist(): + ret1, log = log_os_system("ls "+i2c_prefix+"*0077", 0) + ret2, log = log_os_system("ls "+i2c_prefix+"i2c-2", 0) + return not(ret1 or ret2) + +if __name__ == "__main__": + main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/classes/__init__.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/classes/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/classes/fanutil.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/classes/fanutil.py new file mode 100755 index 000000000000..e001e94092c7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/classes/fanutil.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# 1/10/2018: Jostar modify for as7716_32 +# ------------------------------------------------------------------ + +try: + import time + import logging + from collections import namedtuple +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + + +class FanUtil(object): + """Platform-specific FanUtil class""" + + FAN_NUM_ON_MAIN_BROAD = 6 + FAN_NUM_1_IDX = 1 + FAN_NUM_2_IDX = 2 + FAN_NUM_3_IDX = 3 + FAN_NUM_4_IDX = 4 + FAN_NUM_5_IDX = 5 + FAN_NUM_6_IDX = 6 + + FAN_NODE_NUM_OF_MAP = 2 + FAN_NODE_FAULT_IDX_OF_MAP = 1 + #FAN_NODE_SPEED_IDX_OF_MAP = 2 + FAN_NODE_DIR_IDX_OF_MAP = 2 + #FAN_NODE_DUTY_IDX_OF_MAP = 4 + #FANR_NODE_FAULT_IDX_OF_MAP = 5 + + #BASE_VAL_PATH = '/sys/devices/platform/as5712_54x_fan/{0}' + BASE_VAL_PATH = '/sys/bus/i2c/devices/9-0066/{0}' + FAN_DUTY_PATH = '/sys/bus/i2c/devices/9-0066/fan_duty_cycle_percentage' + + #logfile = '' + #loglevel = logging.INFO + + """ Dictionary where + key1 = fan id index (integer) starting from 1 + key2 = fan node index (interger) starting from 1 + value = path to fan device file (string) """ + _fan_to_device_path_mapping = {} + +#fan1_direction +#fan1_fault +#fan1_present + + #(FAN_NUM_2_IDX, FAN_NODE_DUTY_IDX_OF_MAP): 'fan2_duty_cycle_percentage', + _fan_to_device_node_mapping = { + (FAN_NUM_1_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan1_fault', + (FAN_NUM_1_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan1_direction', + + (FAN_NUM_2_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan2_fault', + (FAN_NUM_2_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan2_direction', + + (FAN_NUM_3_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan3_fault', + (FAN_NUM_3_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan3_direction', + + (FAN_NUM_4_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan4_fault', + (FAN_NUM_4_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan4_direction', + + (FAN_NUM_5_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan5_fault', + (FAN_NUM_5_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan5_direction', + + (FAN_NUM_6_IDX, FAN_NODE_FAULT_IDX_OF_MAP): 'fan6_fault', + (FAN_NUM_6_IDX, FAN_NODE_DIR_IDX_OF_MAP): 'fan6_direction', + } + + def _get_fan_to_device_node(self, fan_num, node_num): + return self._fan_to_device_node_mapping[(fan_num, node_num)] + + def _get_fan_node_val(self, fan_num, node_num): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num:%d', fan_num) + return None + + if node_num < self.FAN_NODE_FAULT_IDX_OF_MAP or node_num > self.FAN_NODE_NUM_OF_MAP: + logging.debug('GET. Parameter error. node_num:%d', node_num) + return None + + device_path = self.get_fan_to_device_path(fan_num, node_num) + + try: + val_file = open(device_path, 'r') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + content = val_file.readline().rstrip() + #print "fan_num=%d" %fan_num + #print "node_num=%d" %node_num + #print "device_path=%s" %device_path + #print "content=%s" %content + + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return int(content) + + def _set_fan_node_val(self, fan_num, node_num, val): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num:%d', fan_num) + return None + + if node_num < self.FAN_NODE_FAULT_IDX_OF_MAP or node_num > self.FAN_NODE_NUM_OF_MAP: + logging.debug('GET. Parameter error. node_num:%d', node_num) + return None + + content = str(val) + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + device_path = self.get_fan_to_device_path(fan_num, node_num) + try: + val_file = open(device_path, 'w') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + val_file.write(content) + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return True + + def __init__(self): + fan_path = self.BASE_VAL_PATH + + for fan_num in range(self.FAN_NUM_1_IDX, self.FAN_NUM_ON_MAIN_BROAD+1): + for node_num in range(self.FAN_NODE_FAULT_IDX_OF_MAP, self.FAN_NODE_NUM_OF_MAP+1): + self._fan_to_device_path_mapping[(fan_num, node_num)] = fan_path.format( + self._fan_to_device_node_mapping[(fan_num, node_num)]) + #self._fan_to_device_path_mapping[(fan_num, node_num)] = fan_path + self._fan_to_device_node_mapping[(fan_num, node_num)]) + #print "fan_num=%d" %fan_num + #print "node_num=%d" %node_num + #print "self._fan_to_device_path_mapping[(fan_num, node_num)]=%s" %self._fan_to_device_path_mapping[(fan_num, node_num)] + + def get_num_fans(self): + return self.FAN_NUM_ON_MAIN_BROAD + + def get_idx_fan_start(self): + return self.FAN_NUM_1_IDX + + def get_num_nodes(self): + return self.FAN_NODE_NUM_OF_MAP + + def get_idx_node_start(self): + return self.FAN_NODE_FAULT_IDX_OF_MAP + + def get_size_node_map(self): + return len(self._fan_to_device_node_mapping) + + def get_size_path_map(self): + return len(self._fan_to_device_path_mapping) + + def get_fan_to_device_path(self, fan_num, node_num): + return self._fan_to_device_path_mapping[(fan_num, node_num)] + + def get_fan_fault(self, fan_num): + return self._get_fan_node_val(fan_num, self.FAN_NODE_FAULT_IDX_OF_MAP) + + #def get_fan_speed(self, fan_num): + # return self._get_fan_node_val(fan_num, self.FAN_NODE_SPEED_IDX_OF_MAP) + + def get_fan_dir(self, fan_num): + return self._get_fan_node_val(fan_num, self.FAN_NODE_DIR_IDX_OF_MAP) + + def get_fan_duty_cycle(self): + #duty_path = self.FAN_DUTY_PATH + try: + val_file = open(self.FAN_DUTY_PATH) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + content = val_file.readline().rstrip() + val_file.close() + + return int(content) + #self._get_fan_node_val(fan_num, self.FAN_NODE_DUTY_IDX_OF_MAP) +#static u32 reg_val_to_duty_cycle(u8 reg_val) +#{ +# reg_val &= FAN_DUTY_CYCLE_REG_MASK; +# return ((u32)(reg_val+1) * 625 + 75)/ 100; +#} +# + def set_fan_duty_cycle(self, val): + + try: + fan_file = open(self.FAN_DUTY_PATH, 'r+') + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + #val = ((val + 1 ) * 625 +75 ) / 100 + fan_file.write(str(val)) + fan_file.close() + return True + + #def get_fanr_fault(self, fan_num): + # return self._get_fan_node_val(fan_num, self.FANR_NODE_FAULT_IDX_OF_MAP) + + def get_fanr_speed(self, fan_num): + return self._get_fan_node_val(fan_num, self.FANR_NODE_SPEED_IDX_OF_MAP) + + def get_fan_status(self, fan_num): + if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. fan_num, %d', fan_num) + print "fan %d return none" %fan_num + return None + + if self.get_fan_fault(fan_num) is not None and self.get_fan_fault(fan_num) > 0: + logging.debug('GET. FAN fault. fan_num, %d', fan_num) + return False + + #if self.get_fanr_fault(fan_num) is not None and self.get_fanr_fault(fan_num) > 0: + # logging.debug('GET. FANR fault. fan_num, %d', fan_num) + # return False + + return True + +#def main(): +# fan = FanUtil() +# +# print 'get_size_node_map : %d' % fan.get_size_node_map() +# print 'get_size_path_map : %d' % fan.get_size_path_map() +# for x in range(fan.get_idx_fan_start(), fan.get_num_fans()+1): +# for y in range(fan.get_idx_node_start(), fan.get_num_nodes()+1): +# print fan.get_fan_to_device_path(x, y) +# +#if __name__ == '__main__': +# main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/classes/thermalutil.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/classes/thermalutil.py new file mode 100755 index 000000000000..d3feda8faad9 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/classes/thermalutil.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# 1/10/2018:Jostar modify for as7716_32x +# ------------------------------------------------------------------ + +try: + import time + import logging + import glob + from collections import namedtuple +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + + +class ThermalUtil(object): + """Platform-specific ThermalUtil class""" + + THERMAL_NUM_ON_MAIN_BROAD = 3 + THERMAL_NUM_1_IDX = 1 # 1_ON_MAIN_BROAD + THERMAL_NUM_2_IDX = 2 # 2_ON_MAIN_BROAD + THERMAL_NUM_3_IDX = 3 # 3_ON_MAIN_BROAD + + BASE_VAL_PATH = '/sys/bus/i2c/devices/{0}-00{1}/hwmon/hwmon*/temp1_input' + + """ Dictionary where + key1 = thermal id index (integer) starting from 1 + value = path to fan device file (string) """ + _thermal_to_device_path_mapping = {} + + _thermal_to_device_node_mapping = { + THERMAL_NUM_1_IDX: ['10', '48'], + THERMAL_NUM_2_IDX: ['10', '49'], + THERMAL_NUM_3_IDX: ['10', '4a'], + } + + def __init__(self): + thermal_path = self.BASE_VAL_PATH + + for x in range(self.THERMAL_NUM_1_IDX, self.THERMAL_NUM_ON_MAIN_BROAD+1): + self._thermal_to_device_path_mapping[x] = thermal_path.format( + self._thermal_to_device_node_mapping[x][0], + self._thermal_to_device_node_mapping[x][1]) + #print "self._thermal_to_device_path_mapping[x]=%s" %self._thermal_to_device_path_mapping[x] + + def _get_thermal_node_val(self, thermal_num): + if thermal_num < self.THERMAL_NUM_1_IDX or thermal_num > self.THERMAL_NUM_ON_MAIN_BROAD: + logging.debug('GET. Parameter error. thermal_num, %d', thermal_num) + return None + + device_path = self.get_thermal_to_device_path(thermal_num) + for filename in glob.glob(device_path): + try: + val_file = open(filename, 'r') + except IOError as e: + logging.error('GET. unable to open file: %s', str(e)) + return None + + content = val_file.readline().rstrip() + + if content == '': + logging.debug('GET. content is NULL. device_path:%s', device_path) + return None + + try: + val_file.close() + except: + logging.debug('GET. unable to close file. device_path:%s', device_path) + return None + + return int(content) + + + def get_num_thermals(self): + return self.THERMAL_NUM_ON_MAIN_BROAD + + def get_idx_thermal_start(self): + return self.THERMAL_NUM_1_IDX + + def get_size_node_map(self): + return len(self._thermal_to_device_node_mapping) + + def get_size_path_map(self): + return len(self._thermal_to_device_path_mapping) + + def get_thermal_to_device_path(self, thermal_num): + return self._thermal_to_device_path_mapping[thermal_num] + + def get_thermal_1_val(self): + return self._get_thermal_node_val(self.THERMAL_NUM_1_IDX) + + def get_thermal_2_val(self): + return self._get_thermal_node_val(self.THERMAL_NUM_2_IDX) + def get_thermal_temp(self): + return (self._get_thermal_node_val(self.THERMAL_NUM_1_IDX) + self._get_thermal_node_val(self.THERMAL_NUM_2_IDX) +self._get_thermal_node_val(self.THERMAL_NUM_3_IDX)) + +#def main(): +# thermal = ThermalUtil() +# +# print 'get_size_node_map : %d' % thermal.get_size_node_map() +# print 'get_size_path_map : %d' % thermal.get_size_path_map() +# for x in range(thermal.get_idx_thermal_start(), thermal.get_num_thermals()+1): +# print thermal.get_thermal_to_device_path(x) +# +#if __name__ == '__main__': +# main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/Makefile b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/Makefile new file mode 100755 index 000000000000..3dfb021a7aaf --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/Makefile @@ -0,0 +1,17 @@ +ifneq ($(KERNELRELEASE),) +obj-m:= accton_as7716_32xb_cpld1.o accton_as7716_32xb_fan.o \ + accton_as7716_32xb_leds.o accton_as7716_32xb_psu.o \ + accton_as7716_32xb_thermal.o accton_as7716_32xb_oom.o accton_as7716_32xb_pmbus.o\ + accton_as7716_32xb_sys.o accton_i2c_cpld.o +else +ifeq (,$(KERNEL_SRC)) +$(error KERNEL_SRC is not defined) +else +KERNELDIR:=$(KERNEL_SRC) +endif +PWD:=$(shell pwd) +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules +clean: + rm -rf *.o *.mod.o *.mod.o *.ko .*cmd .tmp_versions Module.markers Module.symvers modules.order +endif diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_cpld1.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_cpld1.c new file mode 100755 index 000000000000..d2db4fb1f484 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_cpld1.c @@ -0,0 +1,1021 @@ +/* + * A hwmon driver for the as7716_32xbb_cpld + * + * Copyright (C) 2013 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +#define I2C_RW_RETRY_COUNT 10 +#define I2C_RW_RETRY_INTERVAL 60 /* ms */ +#define STRING_TO_DEC_VALUE 10 + +static ssize_t show_present(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_present_all(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t show_version(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t get_mode_reset(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t set_mode_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); + +static int as7716_32xb_cpld_read_internal(struct i2c_client *client, u8 reg); +static int as7716_32xb_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value); + +static ssize_t sfp_value_show(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t sfp_value_store(struct device *dev, struct device_attribute *da, + char *buf, size_t size); + + +#define PORT_NUM_MAX 32 + +struct as7716_32xb_cpld_data { + struct device *hwmon_dev; + struct mutex update_lock; + unsigned int present[PORT_NUM_MAX]; + unsigned int reset[PORT_NUM_MAX]; +}; +enum port_id { + PORT1_ID, + PORT2_ID, + PORT3_ID, + PORT4_ID, + PORT5_ID, + PORT6_ID, + PORT7_ID, + PORT8_ID, + PORT9_ID, + PORT10_ID, + PORT11_ID, + PORT12_ID, + PORT13_ID, + PORT14_ID, + PORT15_ID, + PORT16_ID, + PORT17_ID, + PORT18_ID, + PORT19_ID, + PORT20_ID, + PORT21_ID, + PORT22_ID, + PORT23_ID, + PORT24_ID, + PORT25_ID, + PORT26_ID, + PORT27_ID, + PORT28_ID, + PORT29_ID, + PORT30_ID, + PORT31_ID, + PORT32_ID +}; + +/* Addresses scanned for as7716_32xbb_cpld + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index +#define TRANSCEIVER_RESET_ATTR_ID(index) MODULE_RESET_##index + +enum as7716_32xb_cpld_sysfs_attributes { + CPLD_VERSION, + ACCESS, + MODULE_PRESENT_ALL, + /* transceiver attributes */ + TRANSCEIVER_PRESENT_ATTR_ID(1), + TRANSCEIVER_PRESENT_ATTR_ID(2), + TRANSCEIVER_PRESENT_ATTR_ID(3), + TRANSCEIVER_PRESENT_ATTR_ID(4), + TRANSCEIVER_PRESENT_ATTR_ID(5), + TRANSCEIVER_PRESENT_ATTR_ID(6), + TRANSCEIVER_PRESENT_ATTR_ID(7), + TRANSCEIVER_PRESENT_ATTR_ID(8), + TRANSCEIVER_PRESENT_ATTR_ID(9), + TRANSCEIVER_PRESENT_ATTR_ID(10), + TRANSCEIVER_PRESENT_ATTR_ID(11), + TRANSCEIVER_PRESENT_ATTR_ID(12), + TRANSCEIVER_PRESENT_ATTR_ID(13), + TRANSCEIVER_PRESENT_ATTR_ID(14), + TRANSCEIVER_PRESENT_ATTR_ID(15), + TRANSCEIVER_PRESENT_ATTR_ID(16), + TRANSCEIVER_PRESENT_ATTR_ID(17), + TRANSCEIVER_PRESENT_ATTR_ID(18), + TRANSCEIVER_PRESENT_ATTR_ID(19), + TRANSCEIVER_PRESENT_ATTR_ID(20), + TRANSCEIVER_PRESENT_ATTR_ID(21), + TRANSCEIVER_PRESENT_ATTR_ID(22), + TRANSCEIVER_PRESENT_ATTR_ID(23), + TRANSCEIVER_PRESENT_ATTR_ID(24), + TRANSCEIVER_PRESENT_ATTR_ID(25), + TRANSCEIVER_PRESENT_ATTR_ID(26), + TRANSCEIVER_PRESENT_ATTR_ID(27), + TRANSCEIVER_PRESENT_ATTR_ID(28), + TRANSCEIVER_PRESENT_ATTR_ID(29), + TRANSCEIVER_PRESENT_ATTR_ID(30), + TRANSCEIVER_PRESENT_ATTR_ID(31), + TRANSCEIVER_PRESENT_ATTR_ID(32), + TRANSCEIVER_RESET_ATTR_ID(1), + TRANSCEIVER_RESET_ATTR_ID(2), + TRANSCEIVER_RESET_ATTR_ID(3), + TRANSCEIVER_RESET_ATTR_ID(4), + TRANSCEIVER_RESET_ATTR_ID(5), + TRANSCEIVER_RESET_ATTR_ID(6), + TRANSCEIVER_RESET_ATTR_ID(7), + TRANSCEIVER_RESET_ATTR_ID(8), + TRANSCEIVER_RESET_ATTR_ID(9), + TRANSCEIVER_RESET_ATTR_ID(10), + TRANSCEIVER_RESET_ATTR_ID(11), + TRANSCEIVER_RESET_ATTR_ID(12), + TRANSCEIVER_RESET_ATTR_ID(13), + TRANSCEIVER_RESET_ATTR_ID(14), + TRANSCEIVER_RESET_ATTR_ID(15), + TRANSCEIVER_RESET_ATTR_ID(16), + TRANSCEIVER_RESET_ATTR_ID(17), + TRANSCEIVER_RESET_ATTR_ID(18), + TRANSCEIVER_RESET_ATTR_ID(19), + TRANSCEIVER_RESET_ATTR_ID(20), + TRANSCEIVER_RESET_ATTR_ID(21), + TRANSCEIVER_RESET_ATTR_ID(22), + TRANSCEIVER_RESET_ATTR_ID(23), + TRANSCEIVER_RESET_ATTR_ID(24), + TRANSCEIVER_RESET_ATTR_ID(25), + TRANSCEIVER_RESET_ATTR_ID(26), + TRANSCEIVER_RESET_ATTR_ID(27), + TRANSCEIVER_RESET_ATTR_ID(28), + TRANSCEIVER_RESET_ATTR_ID(29), + TRANSCEIVER_RESET_ATTR_ID(30), + TRANSCEIVER_RESET_ATTR_ID(31), + TRANSCEIVER_RESET_ATTR_ID(32), +}; + +/* sysfs attributes for hwmon + */ + +/* transceiver attributes */ +/*present*/ +#define DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_present_##index, S_IWUSR|S_IRUGO, sfp_value_show, sfp_value_store, MODULE_PRESENT_##index) +#define DECLARE_TRANSCEIVER_ATTR(index) &sensor_dev_attr_module_present_##index.dev_attr.attr + +/*reset*/ +//#define DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(index) \ +// static SENSOR_DEVICE_ATTR(module_reset_##index, S_IWUSR | S_IRUGO, get_mode_reset, set_mode_reset, MODULE_RESET_##index) +//#define DECLARE_TRANSCEIVER_RESET_ATTR(index) &sensor_dev_attr_module_reset_##index.dev_attr.attr +#define DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_reset_##index, S_IWUSR | S_IRUGO, sfp_value_show, sfp_value_store, MODULE_RESET_##index) +#define DECLARE_TRANSCEIVER_RESET_ATTR(index) &sensor_dev_attr_module_reset_##index.dev_attr.attr + + + + +static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_version, NULL, CPLD_VERSION); +static SENSOR_DEVICE_ATTR(access, S_IWUSR, NULL, access, ACCESS); +/* transceiver attributes */ +static SENSOR_DEVICE_ATTR(module_present_all, S_IRUGO, show_present_all, NULL, MODULE_PRESENT_ALL); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(1); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(2); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(3); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(4); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(5); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(6); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(7); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(8); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(9); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(10); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(11); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(12); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(13); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(14); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(15); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(16); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(17); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(18); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(19); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(20); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(21); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(22); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(23); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(24); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(25); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(26); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(27); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(28); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(29); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(30); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(31); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(32); + +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(1); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(2); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(3); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(4); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(5); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(6); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(7); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(8); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(9); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(10); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(11); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(12); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(13); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(14); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(15); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(16); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(17); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(18); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(19); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(20); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(21); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(22); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(23); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(24); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(25); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(26); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(27); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(28); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(29); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(30); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(31); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(32); + + +static struct attribute *as7716_32xb_cpld_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + /* transceiver attributes */ + &sensor_dev_attr_module_present_all.dev_attr.attr, + DECLARE_TRANSCEIVER_ATTR(1), + DECLARE_TRANSCEIVER_ATTR(2), + DECLARE_TRANSCEIVER_ATTR(3), + DECLARE_TRANSCEIVER_ATTR(4), + DECLARE_TRANSCEIVER_ATTR(5), + DECLARE_TRANSCEIVER_ATTR(6), + DECLARE_TRANSCEIVER_ATTR(7), + DECLARE_TRANSCEIVER_ATTR(8), + DECLARE_TRANSCEIVER_ATTR(9), + DECLARE_TRANSCEIVER_ATTR(10), + DECLARE_TRANSCEIVER_ATTR(11), + DECLARE_TRANSCEIVER_ATTR(12), + DECLARE_TRANSCEIVER_ATTR(13), + DECLARE_TRANSCEIVER_ATTR(14), + DECLARE_TRANSCEIVER_ATTR(15), + DECLARE_TRANSCEIVER_ATTR(16), + DECLARE_TRANSCEIVER_ATTR(17), + DECLARE_TRANSCEIVER_ATTR(18), + DECLARE_TRANSCEIVER_ATTR(19), + DECLARE_TRANSCEIVER_ATTR(20), + DECLARE_TRANSCEIVER_ATTR(21), + DECLARE_TRANSCEIVER_ATTR(22), + DECLARE_TRANSCEIVER_ATTR(23), + DECLARE_TRANSCEIVER_ATTR(24), + DECLARE_TRANSCEIVER_ATTR(25), + DECLARE_TRANSCEIVER_ATTR(26), + DECLARE_TRANSCEIVER_ATTR(27), + DECLARE_TRANSCEIVER_ATTR(28), + DECLARE_TRANSCEIVER_ATTR(29), + DECLARE_TRANSCEIVER_ATTR(30), + DECLARE_TRANSCEIVER_ATTR(31), + DECLARE_TRANSCEIVER_ATTR(32), + DECLARE_TRANSCEIVER_RESET_ATTR(1), + DECLARE_TRANSCEIVER_RESET_ATTR(2), + DECLARE_TRANSCEIVER_RESET_ATTR(3), + DECLARE_TRANSCEIVER_RESET_ATTR(4), + DECLARE_TRANSCEIVER_RESET_ATTR(5), + DECLARE_TRANSCEIVER_RESET_ATTR(6), + DECLARE_TRANSCEIVER_RESET_ATTR(7), + DECLARE_TRANSCEIVER_RESET_ATTR(8), + DECLARE_TRANSCEIVER_RESET_ATTR(9), + DECLARE_TRANSCEIVER_RESET_ATTR(10), + DECLARE_TRANSCEIVER_RESET_ATTR(11), + DECLARE_TRANSCEIVER_RESET_ATTR(12), + DECLARE_TRANSCEIVER_RESET_ATTR(13), + DECLARE_TRANSCEIVER_RESET_ATTR(14), + DECLARE_TRANSCEIVER_RESET_ATTR(15), + DECLARE_TRANSCEIVER_RESET_ATTR(16), + DECLARE_TRANSCEIVER_RESET_ATTR(17), + DECLARE_TRANSCEIVER_RESET_ATTR(18), + DECLARE_TRANSCEIVER_RESET_ATTR(19), + DECLARE_TRANSCEIVER_RESET_ATTR(20), + DECLARE_TRANSCEIVER_RESET_ATTR(21), + DECLARE_TRANSCEIVER_RESET_ATTR(22), + DECLARE_TRANSCEIVER_RESET_ATTR(23), + DECLARE_TRANSCEIVER_RESET_ATTR(24), + DECLARE_TRANSCEIVER_RESET_ATTR(25), + DECLARE_TRANSCEIVER_RESET_ATTR(26), + DECLARE_TRANSCEIVER_RESET_ATTR(27), + DECLARE_TRANSCEIVER_RESET_ATTR(28), + DECLARE_TRANSCEIVER_RESET_ATTR(29), + DECLARE_TRANSCEIVER_RESET_ATTR(30), + DECLARE_TRANSCEIVER_RESET_ATTR(31), + DECLARE_TRANSCEIVER_RESET_ATTR(32), + NULL +}; + +static const struct attribute_group as7716_32xb_cpld_group = { + .attrs = as7716_32xb_cpld_attributes, +}; + +static ssize_t show_present_all(struct device *dev, struct device_attribute *da, + char *buf) +{ + int i, status; + u8 values[4] = {0}; + u8 regs[] = {0x30, 0x31, 0x32, 0x33}; + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_cpld_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + status = as7716_32xb_cpld_read_internal(client, regs[i]); + + if (status < 0) { + goto exit; + } + + values[i] = ~(u8)status; + } + + mutex_unlock(&data->update_lock); + + /* Return values 1 -> 32 in order */ + return sprintf(buf, "%.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], + values[3]); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static int sfp_array_index_get(int attr_idx) +{ + switch(attr_idx) + { + case TRANSCEIVER_PRESENT_ATTR_ID(1): + case TRANSCEIVER_RESET_ATTR_ID(1): + return PORT1_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(2): + case TRANSCEIVER_RESET_ATTR_ID(2): + return PORT2_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(3): + case TRANSCEIVER_RESET_ATTR_ID(3): + return PORT3_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(4): + case TRANSCEIVER_RESET_ATTR_ID(4): + return PORT4_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(5): + case TRANSCEIVER_RESET_ATTR_ID(5): + return PORT5_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(6): + case TRANSCEIVER_RESET_ATTR_ID(6): + return PORT6_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(7): + case TRANSCEIVER_RESET_ATTR_ID(7): + return PORT7_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(8): + case TRANSCEIVER_RESET_ATTR_ID(8): + return PORT8_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(9): + case TRANSCEIVER_RESET_ATTR_ID(9): + return PORT9_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(10): + case TRANSCEIVER_RESET_ATTR_ID(10): + return PORT10_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(11): + case TRANSCEIVER_RESET_ATTR_ID(11): + return PORT11_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(12): + case TRANSCEIVER_RESET_ATTR_ID(12): + return PORT12_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(13): + case TRANSCEIVER_RESET_ATTR_ID(13): + return PORT13_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(14): + case TRANSCEIVER_RESET_ATTR_ID(14): + return PORT14_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(15): + case TRANSCEIVER_RESET_ATTR_ID(15): + return PORT15_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(16): + case TRANSCEIVER_RESET_ATTR_ID(16): + return PORT16_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(17): + case TRANSCEIVER_RESET_ATTR_ID(17): + return PORT17_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(18): + case TRANSCEIVER_RESET_ATTR_ID(18): + return PORT18_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(19): + case TRANSCEIVER_RESET_ATTR_ID(19): + return PORT19_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(20): + case TRANSCEIVER_RESET_ATTR_ID(20): + return PORT20_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(21): + case TRANSCEIVER_RESET_ATTR_ID(21): + return PORT21_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(22): + case TRANSCEIVER_RESET_ATTR_ID(22): + return PORT22_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(23): + case TRANSCEIVER_RESET_ATTR_ID(23): + return PORT23_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(24): + case TRANSCEIVER_RESET_ATTR_ID(24): + return PORT24_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(25): + case TRANSCEIVER_RESET_ATTR_ID(25): + return PORT25_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(26): + case TRANSCEIVER_RESET_ATTR_ID(26): + return PORT26_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(27): + case TRANSCEIVER_RESET_ATTR_ID(27): + return PORT27_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(28): + case TRANSCEIVER_RESET_ATTR_ID(28): + return PORT28_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(29): + case TRANSCEIVER_RESET_ATTR_ID(29): + return PORT29_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(30): + case TRANSCEIVER_RESET_ATTR_ID(30): + return PORT30_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(31): + case TRANSCEIVER_RESET_ATTR_ID(31): + return PORT31_ID; + case TRANSCEIVER_PRESENT_ATTR_ID(32): + case TRANSCEIVER_RESET_ATTR_ID(32): + return PORT32_ID; + default : + return -1; + } +} + + +static ssize_t sfp_value_store(struct device *dev, struct device_attribute *da, + char *buf, size_t size) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_cpld_data *data = i2c_get_clientdata(client); + int status = -EINVAL; + int index; + long keyin = 0; + + //printk("sfp_value_store\n"); + //printk("attr->index=%d\n", attr->index); + mutex_lock(&data->update_lock); + switch (attr->index) + { + case MODULE_PRESENT_1 ... MODULE_PRESENT_8: + case MODULE_PRESENT_9 ... MODULE_PRESENT_16: + case MODULE_PRESENT_17 ... MODULE_PRESENT_24: + case MODULE_PRESENT_25 ... MODULE_PRESENT_32: + index=sfp_array_index_get(attr->index); + if(index < 0 || index > PORT_NUM_MAX -1) + break; + status = kstrtol(buf, STRING_TO_DEC_VALUE, &keyin); + if(keyin < 0 || keyin > 1) + break; + data->present[index]=keyin; + break; + case MODULE_RESET_1 ... MODULE_RESET_8: + case MODULE_RESET_9 ... MODULE_RESET_16: + case MODULE_RESET_17 ... MODULE_RESET_24: + case MODULE_RESET_25 ... MODULE_RESET_32: + index=sfp_array_index_get(attr->index); + if(index < 0 || index > PORT_NUM_MAX -1) + break; + status = kstrtol(buf, STRING_TO_DEC_VALUE, &keyin); + if(keyin < 0 || keyin > 1) + break; + data->reset[index]=keyin; + break; + default : + break; + } + mutex_unlock(&data->update_lock); + return size; +} +static ssize_t sfp_value_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_cpld_data *data = i2c_get_clientdata(client); + ssize_t ret = 0; + int index; + int status = -EINVAL; + + //printk("sfp_value_show, attr->index=%d\n", attr->index); + //printk("TRANSCEIVER_PRESENT_ATTR_ID(1)=%d, TRANSCEIVER_RESET_ATTR_ID(1)=%d\n", TRANSCEIVER_PRESENT_ATTR_ID(1), TRANSCEIVER_RESET_ATTR_ID(1)); + mutex_lock(&data->update_lock); + switch (attr->index) + { + case MODULE_PRESENT_1 ... MODULE_PRESENT_8: + case MODULE_PRESENT_9 ... MODULE_PRESENT_16: + case MODULE_PRESENT_17 ... MODULE_PRESENT_24: + case MODULE_PRESENT_25 ... MODULE_PRESENT_32: + index=sfp_array_index_get(attr->index); + if(index < 0 || index > PORT_NUM_MAX -1) + break; + status = sprintf(buf, "%u\n", data->present[index]); + break; + case MODULE_RESET_1 ... MODULE_RESET_8: + case MODULE_RESET_9 ... MODULE_RESET_16: + case MODULE_RESET_17 ... MODULE_RESET_24: + case MODULE_RESET_25 ... MODULE_RESET_32: + index=sfp_array_index_get(attr->index); + //printk("rst:attr->index=%d, index=%d\n",attr->index, index); + if(index < 0 || index > PORT_NUM_MAX -1) + break; + status = sprintf(buf, "%u\n", data->reset[index]); + break; + default : + break; + } + + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_present(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_cpld_data *data = i2c_get_clientdata(client); + int status = 0; + u8 reg = 0, mask = 0; + + switch (attr->index) { + case MODULE_PRESENT_1 ... MODULE_PRESENT_8: + reg = 0x30; + mask = 0x1 << (attr->index - MODULE_PRESENT_1); + break; + case MODULE_PRESENT_9 ... MODULE_PRESENT_16: + reg = 0x31; + mask = 0x1 << (attr->index - MODULE_PRESENT_9); + break; + case MODULE_PRESENT_17 ... MODULE_PRESENT_24: + reg = 0x32; + mask = 0x1 << (attr->index - MODULE_PRESENT_17); + break; + case MODULE_PRESENT_25 ... MODULE_PRESENT_32: + reg = 0x33; + mask = 0x1 << (attr->index - MODULE_PRESENT_25); + break; + default: + return 0; + } + + + mutex_lock(&data->update_lock); + status = as7716_32xb_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", !(status & mask)); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_version(struct device *dev, struct device_attribute *da, + char *buf) +{ + u8 reg = 0, mask = 0; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_cpld_data *data = i2c_get_clientdata(client); + int status = 0; + + switch (attr->index) { + case CPLD_VERSION: + reg = 0x1; + mask = 0xFF; + break; + default: + break; + } + + mutex_lock(&data->update_lock); + status = as7716_32xb_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d\n", (status & mask)); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int status; + u32 addr, val; + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_cpld_data *data = i2c_get_clientdata(client); + + if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) { + return -EINVAL; + } + + if (addr > 0xFF || val > 0xFF) { + return -EINVAL; + } + + mutex_lock(&data->update_lock); + status = as7716_32xb_cpld_write_internal(client, addr, val); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static int as7716_32xb_cpld_read_internal(struct i2c_client *client, u8 reg) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, reg); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static int as7716_32xb_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_write_byte_data(client, reg, value); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static void as7716_32xb_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void as7716_32xb_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + + mutex_unlock(&list_lock); +} + +static int as7716_32xb_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + struct as7716_32xb_cpld_data *data = NULL; + + data = kzalloc(sizeof(struct as7716_32xb_cpld_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7716_32xb_cpld_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + as7716_32xb_cpld_add_client(client); + + dev_info(&client->dev, "%s: cpld '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_cpld_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7716_32xb_cpld_remove(struct i2c_client *client) +{ + struct as7716_32xb_cpld_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_cpld_group); + kfree(data); + as7716_32xb_cpld_remove_client(client); + + return 0; +} + +int as7716_32xb_cpld_read(unsigned short cpld_addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = i2c_smbus_read_byte_data(cpld_node->client, reg); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as7716_32xb_cpld_read); + +int as7716_32xb_cpld_write(unsigned short cpld_addr, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = i2c_smbus_write_byte_data(cpld_node->client, reg, value); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as7716_32xb_cpld_write); + +static ssize_t get_mode_reset(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_cpld_data *data = i2c_get_clientdata(client); + int status = 0; + u8 reg = 0, mask = 0; + + switch (attr->index) { + case MODULE_RESET_1 ... MODULE_RESET_8: + reg = 0x04; + mask = 0x1 << (attr->index - MODULE_RESET_1); + break; + case MODULE_RESET_9 ... MODULE_RESET_16: + reg = 0x05; + mask = 0x1 << (attr->index - MODULE_RESET_9); + break; + case MODULE_RESET_17 ... MODULE_RESET_24: + reg = 0x06; + mask = 0x1 << (attr->index - MODULE_RESET_17); + break; + case MODULE_RESET_25 ... MODULE_RESET_32: + reg = 0x07; + mask = 0x1 << (attr->index - MODULE_RESET_25); + break; + default: + return 0; + } + + + mutex_lock(&data->update_lock); + status = as7716_32xb_cpld_read_internal(client, reg); + + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\r\n", !(status & mask)); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t set_mode_reset(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_cpld_data *data = i2c_get_clientdata(client); + long reset; + int status=0, val, error; + u8 reg = 0, mask = 0; + + + error = kstrtol(buf, 10, &reset); + if (error) { + return error; + } + //printk("set_mode_reset:attr->index=%d\n",attr->index); + switch (attr->index) { + case MODULE_RESET_1 ... MODULE_RESET_8: + reg = 0x04; + mask = 0x1 << (attr->index - MODULE_RESET_1); + break; + case MODULE_RESET_9 ... MODULE_RESET_16: + reg = 0x05; + mask = 0x1 << (attr->index - MODULE_RESET_9); + break; + case MODULE_RESET_17 ... MODULE_RESET_24: + reg = 0x06; + mask = 0x1 << (attr->index - MODULE_RESET_17); + break; + case MODULE_RESET_25 ... MODULE_RESET_32: + reg = 0x07; + mask = 0x1 << (attr->index - MODULE_RESET_25); + break; + default: + return 0; + } + mutex_lock(&data->update_lock); + + status = as7716_32xb_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + //printk("set_mode_reset:reset=%d, reg=0x%x, mask=0x%x, ori_val=0x%x\n", reset, reg, mask, status); + /* Update lp_mode status */ + if (reset) + { + val = status&(~mask); + //printk("1:new val=0x%x\n", val); + } + else + { + val =status | (mask); + //printk("0:new val=0x%x\n", val); + } + + status = as7716_32xb_cpld_write_internal(client, reg, val); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + + + +static const struct i2c_device_id as7716_32xb_cpld_id[] = { + { "as7716_32xb_cpld1", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7716_32xb_cpld_id); + +static struct i2c_driver as7716_32xb_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7716_32xb_cpld1", + }, + .probe = as7716_32xb_cpld_probe, + .remove = as7716_32xb_cpld_remove, + .id_table = as7716_32xb_cpld_id, + .address_list = normal_i2c, +}; + +static int __init as7716_32xb_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&as7716_32xb_cpld_driver); +} + +static void __exit as7716_32xb_cpld_exit(void) +{ + i2c_del_driver(&as7716_32xb_cpld_driver); +} + +module_init(as7716_32xb_cpld_init); +module_exit(as7716_32xb_cpld_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7716_32xb_cpld driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_fan.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_fan.c new file mode 100755 index 000000000000..5c1c3016c3a1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_fan.c @@ -0,0 +1,969 @@ +/* + * A hwmon driver for the Accton as7716 32x fan + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as7716_32xb_fan" + +#define NUM_THERMAL_SENSORS (3) /* Get sum of this number of sensors.*/ +#define THERMAL_SENSORS_DRIVER "lm75" +#define STRING_TO_DEC_VALUE 10 + +#define IN +#define OUT + +static struct as7716_32xb_fan_data *as7716_32xb_fan_update_device(struct device *dev); +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t get_enable(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t get_sys_temp(struct device *dev, struct device_attribute *da, char *buf); + + +static ssize_t fan_value_show(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t fan_value_store(struct device *dev, struct device_attribute *da, + char *buf, size_t size); + +/* fan related data, the index should match sysfs_fan_attributes + */ +static const u8 fan_reg[] = { + 0x0F, /* fan 1-6 present status */ + 0x10, /* fan 1-6 direction(0:B2F 1:F2B) */ + 0x11, /* fan PWM(for all fan) */ + 0x12, /* front fan 1 speed(rpm) */ + 0x13, /* front fan 2 speed(rpm) */ + 0x14, /* front fan 3 speed(rpm) */ + 0x15, /* front fan 4 speed(rpm) */ + 0x16, /* front fan 5 speed(rpm) */ + 0x17, /* front fan 6 speed(rpm) */ + 0x22, /* rear fan 1 speed(rpm) */ + 0x23, /* rear fan 2 speed(rpm) */ + 0x24, /* rear fan 3 speed(rpm) */ + 0x25, /* rear fan 4 speed(rpm) */ + 0x26, /* rear fan 5 speed(rpm) */ + 0x27, /* rear fan 6 speed(rpm) */ +}; + +#define FAN_NUM_MAX 6 +/* Each client has this additional data */ +struct as7716_32xb_fan_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[ARRAY_SIZE(fan_reg)]; /* Register value */ + u8 enable; + u8 duty_cycle; + int system_temp; /*In unit of mini-Celsius*/ + int sensors_found; + unsigned int present[FAN_NUM_MAX]; + unsigned int front_speed_rpm[FAN_NUM_MAX]; + unsigned int rear_speed_rpm[FAN_NUM_MAX]; + unsigned int direction[FAN_NUM_MAX]; + unsigned int fault[FAN_NUM_MAX]; + unsigned int input[FAN_NUM_MAX]; +}; + +enum FAN_ID { + FAN1_ID, + FAN2_ID, + FAN3_ID, + FAN4_ID, + FAN5_ID, + FAN6_ID +}; + +enum sysfs_fan_attributes { + FAN_PRESENT_REG, + FAN_DIRECTION_REG, + FAN_DUTY_CYCLE_PERCENTAGE, /* Only one CPLD register to control duty cycle for all fans */ + FAN1_FRONT_SPEED_RPM, + FAN2_FRONT_SPEED_RPM, + FAN3_FRONT_SPEED_RPM, + FAN4_FRONT_SPEED_RPM, + FAN5_FRONT_SPEED_RPM, + FAN6_FRONT_SPEED_RPM, + FAN1_REAR_SPEED_RPM, + FAN2_REAR_SPEED_RPM, + FAN3_REAR_SPEED_RPM, + FAN4_REAR_SPEED_RPM, + FAN5_REAR_SPEED_RPM, + FAN6_REAR_SPEED_RPM, + FAN1_DIRECTION, + FAN2_DIRECTION, + FAN3_DIRECTION, + FAN4_DIRECTION, + FAN5_DIRECTION, + FAN6_DIRECTION, + FAN1_PRESENT, + FAN2_PRESENT, + FAN3_PRESENT, + FAN4_PRESENT, + FAN5_PRESENT, + FAN6_PRESENT, + FAN1_FAULT, + FAN2_FAULT, + FAN3_FAULT, + FAN4_FAULT, + FAN5_FAULT, + FAN6_FAULT +}; + +/* Define attributes + */ +#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO|S_IWUSR, fan_value_show, fan_value_store, FAN##index##_FAULT);\ + static SENSOR_DEVICE_ATTR(fan##index2##_fault, S_IRUGO|S_IWUSR, fan_value_show, fan_value_store, FAN##index##_FAULT) +#define DECLARE_FAN_FAULT_ATTR(index, index2) &sensor_dev_attr_fan##index##_fault.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_fault.dev_attr.attr + +#define DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_direction, S_IRUGO|S_IWUSR, fan_value_show, fan_value_store, FAN##index##_DIRECTION) +#define DECLARE_FAN_DIRECTION_ATTR(index) &sensor_dev_attr_fan##index##_direction.dev_attr.attr + + +#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_value_show, fan_value_store, FAN_DUTY_CYCLE_PERCENTAGE);\ + static SENSOR_DEVICE_ATTR(pwm##index, S_IWUSR | S_IRUGO, fan_value_show, set_duty_cycle, FAN_DUTY_CYCLE_PERCENTAGE);\ + static SENSOR_DEVICE_ATTR(pwm##index##_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, FAN_DUTY_CYCLE_PERCENTAGE) + +#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) &sensor_dev_attr_fan_duty_cycle_percentage.dev_attr.attr, \ + &sensor_dev_attr_pwm##index.dev_attr.attr, \ + &sensor_dev_attr_pwm##index##_enable.dev_attr.attr + +#define DECLARE_FAN_SYSTEM_TEMP_SENSOR_DEV_ATTR() \ + static SENSOR_DEVICE_ATTR(sys_temp, S_IWUSR|S_IRUGO, get_sys_temp, NULL, FAN_DUTY_CYCLE_PERCENTAGE) + +#define DECLARE_FAN_SYSTEM_TEMP_ATTR() &sensor_dev_attr_sys_temp.dev_attr.attr + + + +#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_present, S_IWUSR|S_IRUGO, fan_value_show, fan_value_store, FAN##index##_PRESENT) +#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr + + +#define DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_front_speed_rpm, S_IWUSR|S_IRUGO, fan_value_show, fan_value_store, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_rear_speed_rpm, S_IWUSR|S_IRUGO, fan_value_show, fan_value_store, FAN##index##_REAR_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_input, S_IWUSR|S_IRUGO, fan_value_show, fan_value_store, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index2##_input, S_IWUSR|S_IRUGO, fan_value_show, fan_value_store, FAN##index##_REAR_SPEED_RPM) +#define DECLARE_FAN_SPEED_RPM_ATTR(index, index2) &sensor_dev_attr_fan##index##_front_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_rear_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_input.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_input.dev_attr.attr + +/* 6 fan fault attributes in this platform */ +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1,11); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2,12); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(3,13); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(4,14); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(5,15); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(6,16); +/* 6 fan speed(rpm) attributes in this platform */ +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1,11); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2,12); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(3,13); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(4,14); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(5,15); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(6,16); +/* 6 fan present attributes in this platform */ +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(5); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(6); + +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(1); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(2); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(3); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(4); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(5); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(6); + +/* 1 fan duty cycle attribute in this platform */ +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(1); +/* System temperature for fancontrol */ +DECLARE_FAN_SYSTEM_TEMP_SENSOR_DEV_ATTR(); + +static struct attribute *as7716_32xb_fan_attributes[] = { + /* fan related attributes */ + DECLARE_FAN_FAULT_ATTR(1,11), + DECLARE_FAN_FAULT_ATTR(2,12), + DECLARE_FAN_FAULT_ATTR(3,13), + DECLARE_FAN_FAULT_ATTR(4,14), + DECLARE_FAN_FAULT_ATTR(5,15), + DECLARE_FAN_FAULT_ATTR(6,16), + DECLARE_FAN_SPEED_RPM_ATTR(1,11), + DECLARE_FAN_SPEED_RPM_ATTR(2,12), + DECLARE_FAN_SPEED_RPM_ATTR(3,13), + DECLARE_FAN_SPEED_RPM_ATTR(4,14), + DECLARE_FAN_SPEED_RPM_ATTR(5,15), + DECLARE_FAN_SPEED_RPM_ATTR(6,16), + DECLARE_FAN_PRESENT_ATTR(1), + DECLARE_FAN_PRESENT_ATTR(2), + DECLARE_FAN_PRESENT_ATTR(3), + DECLARE_FAN_PRESENT_ATTR(4), + DECLARE_FAN_PRESENT_ATTR(5), + DECLARE_FAN_PRESENT_ATTR(6), + DECLARE_FAN_DIRECTION_ATTR(1), + DECLARE_FAN_DIRECTION_ATTR(2), + DECLARE_FAN_DIRECTION_ATTR(3), + DECLARE_FAN_DIRECTION_ATTR(4), + DECLARE_FAN_DIRECTION_ATTR(5), + DECLARE_FAN_DIRECTION_ATTR(6), + DECLARE_FAN_DUTY_CYCLE_ATTR(1), + DECLARE_FAN_SYSTEM_TEMP_ATTR(), + NULL +}; + +#define FAN_DUTY_CYCLE_REG_MASK 0xF +#define FAN_MAX_DUTY_CYCLE 100 +#define FAN_REG_VAL_TO_SPEED_RPM_STEP 100 + +static int as7716_32xb_fan_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int as7716_32xb_fan_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* fan utility functions + */ +static u32 reg_val_to_duty_cycle(u8 reg_val) +{ + reg_val &= FAN_DUTY_CYCLE_REG_MASK; + return ((u32)(reg_val+1) * 625 + 75)/ 100; +} + +static u8 duty_cycle_to_reg_val(u8 duty_cycle) +{ + return ((u32)duty_cycle * 100 / 625) - 1; +} + +static u32 reg_val_to_speed_rpm(u8 reg_val) +{ + return (u32)reg_val * FAN_REG_VAL_TO_SPEED_RPM_STEP; +} + +static u8 reg_val_to_direction(u8 reg_val, enum FAN_ID id) +{ + u8 mask = (1 << id); + + reg_val &= mask; + + return reg_val ? 1 : 0; +} +static u8 reg_val_to_is_present(u8 reg_val, enum FAN_ID id) +{ + u8 mask = (1 << id); + + reg_val &= mask; + + return reg_val ? 0 : 1; +} + +static u8 is_fan_fault(struct as7716_32xb_fan_data *data, enum FAN_ID id) +{ + u8 ret = 0; + + /* Check if the speed of front or rear fan is ZERO, + */ + if (!data->front_speed_rpm[id] && + !data->rear_speed_rpm[id] ) + { + ret = 1; + } + + return ret; +} + + +static ssize_t set_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct as7716_32xb_fan_data *data = as7716_32xb_fan_update_device(dev); + int error, value; + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < 0 || value > 1) + return -EINVAL; + + data->enable = value; + if (value == 0) + { + return set_duty_cycle(dev, da, buf, FAN_MAX_DUTY_CYCLE); + } + return count; +} + + + +static ssize_t get_enable(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as7716_32xb_fan_data *data = as7716_32xb_fan_update_device(dev); + + return sprintf(buf, "%u\n", data->enable); +} + +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int error, value; + struct i2c_client *client = to_i2c_client(dev); + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < 0 || value > FAN_MAX_DUTY_CYCLE) + return -EINVAL; + + as7716_32xb_fan_write_value(client, 0x33, 0); /* Disable fan speed watch dog */ + as7716_32xb_fan_write_value(client, fan_reg[FAN_DUTY_CYCLE_PERCENTAGE], duty_cycle_to_reg_val(value)); + return count; +} + +/* Due to this struct is declared at lm75.c, it cannot be include + * under Sonic environment. I duplicate it from lm75.c. + */ +struct lm75_data { + struct i2c_client *client; + struct device *hwmon_dev; + struct thermal_zone_device *tz; + struct mutex update_lock; + u8 orig_conf; + u8 resolution; /* In bits, between 9 and 12 */ + u8 resolution_limits; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long sample_time; /* In jiffies */ + s16 temp[3]; /* Register values, + 0 = input + 1 = max + 2 = hyst */ +}; + +/*Copied from lm75.c*/ +static inline long lm75_reg_to_mc(s16 temp, u8 resolution) +{ + return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); +} + +/*Get hwmon_dev from i2c_client, set hwmon_dev = NULL is failed.*/ +static struct device * get_hwmon_dev( + struct i2c_client *client) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + if(data) + { + if( data->valid == 1 && data->hwmon_dev) + { + return data->hwmon_dev; + } + + } + return NULL; +} + +/* To find hwmon index by opening hwmon under that i2c address. + */ +static int find_hwmon_index_by_FileOpen( + int bus_nr, + unsigned short addr, + OUT int *index) +{ +#define MAX_HWMON_DEVICE (10) /* Find hwmon device in 0~10*/ + struct file *sfd; + char client_name[96]; + int i=0; + + do { + snprintf(client_name, sizeof(client_name), + "/sys/bus/i2c/devices/%d-%04x/hwmon/hwmon%d/temp1_input", + bus_nr, addr, i); + + sfd = filp_open(client_name, O_RDONLY, 0); + i++; + } while( IS_ERR(sfd) && i < MAX_HWMON_DEVICE); + + if (IS_ERR(sfd)) { + pr_err("Failed to open file(%s)#%d\r\n", client_name, __LINE__); + return -ENOENT; + } + filp_close(sfd, 0); + *index = i - 1; + return 0; + +#undef MAX_HWMON_DEVICE +} + +static int get_temp_file_path( + int bus_nr, unsigned short addr, + struct device *hwmon_dev + ,char *path, int max_len) +{ + + if(hwmon_dev && strlen(dev_name(hwmon_dev))) + { + snprintf(path, max_len, + "/sys/bus/i2c/devices/%d-%04x/hwmon/%s/temp1_input", + bus_nr, addr, dev_name(hwmon_dev)); + } + else + { + int i=0; + if(find_hwmon_index_by_FileOpen( bus_nr, addr, &i)) + { + return -EIO; + } + snprintf(path, max_len, + "/sys/bus/i2c/devices/%d-%04x/hwmon/hwmon%d/temp1_input", + bus_nr, addr, i); + } + return 0; +} + +/*File read the dev file at user space.*/ +static int read_devfile_temp1_input( + struct device *dev, + int bus_nr, + unsigned short addr, + struct device *hwmon_dev, + int *miniCelsius) +{ + struct file *sfd; + char buffer[96]; + char devfile[96]; + int rc, status; + int rdlen, value; + mm_segment_t old_fs; + + rc = 0; + get_temp_file_path(bus_nr, addr, hwmon_dev, devfile, sizeof(devfile)); + sfd = filp_open(devfile, O_RDONLY, 0); + if (IS_ERR(sfd)) { + pr_err("Failed to open file(%s)#%d\r\n", devfile, __LINE__); + return -ENOENT; + } + dev_dbg(dev, "Found device:%s\n",devfile); + + if(!(sfd->f_op) || !(sfd->f_op->read) ) { + pr_err("file %s cann't readable ?\n",devfile); + return -ENOENT; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + rdlen = sfd->f_op->read(sfd, buffer, sizeof(buffer), &sfd->f_pos); + if (rdlen == 0) { + pr_err( "File(%s) empty!\n", devfile); + rc = -EIO; + goto exit; + } + status = sscanf(buffer, "%d", &value); + if (status != 1) { + rc = -EIO; + goto exit; + } + *miniCelsius = value; + dev_dbg(dev,"found sensors: %d @i2c %d-%04x\n", value, bus_nr, addr); + +exit: + set_fs(old_fs); + filp_close(sfd, 0); + return rc; +} + +static u8 is_lm75_data_due(struct i2c_client *client) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + if (time_after(jiffies, data->last_updated + data->sample_time)) + { + return 1; + } + return 0; +} +static int get_lm75_temp(struct i2c_client *client, int *miniCelsius) +{ + struct lm75_data *data = NULL; + + data = i2c_get_clientdata(client); + *miniCelsius = lm75_reg_to_mc(data->temp[0], data->resolution); + + return 0; +} + +static int _find_lm75_device(struct device *dev, void *data) +{ + struct device_driver *driver; + struct as7716_32xb_fan_data *prv = data; + char *driver_name = THERMAL_SENSORS_DRIVER; + + driver = dev->driver; + if (driver && driver->name && + strcmp(driver->name, driver_name) == 0) + { + struct i2c_client *client; + client = to_i2c_client(dev); + if (client) + { + /*cannot use "struct i2c_adapter *adap = to_i2c_adapter(dev);"*/ + struct i2c_adapter *adap = client->adapter; + int miniCelsius = 0; + + + if (!adap) { + return -ENXIO; + } + + /* If the data is not updated, read them from devfile + to drive them updateing data from chip.*/ + if (is_lm75_data_due(client)) + { + struct device *hwmon_dev; + + hwmon_dev = get_hwmon_dev(client); + if(0 == read_devfile_temp1_input(dev, adap->nr, + client->addr, hwmon_dev, &miniCelsius)) + { + prv->system_temp += miniCelsius; + prv->sensors_found++; + } + + } + else + { + get_lm75_temp(client, &miniCelsius); + prv->system_temp += miniCelsius; + prv->sensors_found++; + + } + } + } + return 0; +} + +/*Find all lm75 devices and return sum of temperatures.*/ +static ssize_t get_sys_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + ssize_t ret = 0; + struct as7716_32xb_fan_data *data = as7716_32xb_fan_update_device(dev); + + data->system_temp=0; + data->sensors_found=0; + i2c_for_each_dev(data, _find_lm75_device); + if (NUM_THERMAL_SENSORS != data->sensors_found) + { + dev_dbg(dev,"only %d of %d temps are found\n", + data->sensors_found, NUM_THERMAL_SENSORS); + data->system_temp = 0; + } + ret = sprintf(buf, "%d\n",data->system_temp); + return ret; +} + + +static int fan_array_index_get(int attr_idx) +{ + switch(attr_idx) + { + case FAN1_PRESENT: + case FAN1_FRONT_SPEED_RPM: + case FAN1_REAR_SPEED_RPM: + case FAN1_FAULT: + case FAN1_DIRECTION: + return FAN1_ID; + case FAN2_PRESENT: + case FAN2_FRONT_SPEED_RPM: + case FAN2_REAR_SPEED_RPM: + case FAN2_FAULT: + case FAN2_DIRECTION: + return FAN2_ID; + case FAN3_PRESENT: + case FAN3_FRONT_SPEED_RPM: + case FAN3_REAR_SPEED_RPM: + case FAN3_FAULT: + case FAN3_DIRECTION: + return FAN3_ID; + case FAN4_PRESENT: + case FAN4_FRONT_SPEED_RPM: + case FAN4_REAR_SPEED_RPM: + case FAN4_FAULT: + case FAN4_DIRECTION: + return FAN4_ID; + case FAN5_PRESENT: + case FAN5_FRONT_SPEED_RPM: + case FAN5_REAR_SPEED_RPM: + case FAN5_FAULT: + case FAN5_DIRECTION: + return FAN5_ID; + case FAN6_PRESENT: + case FAN6_FRONT_SPEED_RPM: + case FAN6_REAR_SPEED_RPM: + case FAN6_FAULT: + case FAN6_DIRECTION: + return FAN6_ID; + default : + return -1; + } +} + +static ssize_t fan_value_store(struct device *dev, struct device_attribute *da, + char *buf, size_t size) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_fan_data *data = i2c_get_clientdata(client); + int status = -EINVAL; + int index; + long keyin = 0; + //printk("fan_value_store\n"); + //printk("attr->index=%d\n", attr->index); + mutex_lock(&data->update_lock); + switch (attr->index) + { + case FAN_DUTY_CYCLE_PERCENTAGE: + status = kstrtol(buf, STRING_TO_DEC_VALUE, &keyin); + if (status) + break; + if (keyin > 1 || keyin < 0) + break; + data->duty_cycle=keyin; + break; + case FAN1_FRONT_SPEED_RPM: + case FAN2_FRONT_SPEED_RPM: + case FAN3_FRONT_SPEED_RPM: + case FAN4_FRONT_SPEED_RPM: + case FAN5_FRONT_SPEED_RPM: + case FAN6_FRONT_SPEED_RPM: + status = kstrtol(buf, STRING_TO_DEC_VALUE, &keyin); + if (status) + break; + index=fan_array_index_get(attr->index); + if(index < 0 || index > (FAN_NUM_MAX-1)) + break; + data->front_speed_rpm[index]=keyin; + break; + case FAN1_REAR_SPEED_RPM: + case FAN2_REAR_SPEED_RPM: + case FAN3_REAR_SPEED_RPM: + case FAN4_REAR_SPEED_RPM: + case FAN5_REAR_SPEED_RPM: + case FAN6_REAR_SPEED_RPM: + status = kstrtol(buf, STRING_TO_DEC_VALUE, &keyin); + if (status) + break; + index=fan_array_index_get(attr->index); + if(index < 0 || index > (FAN_NUM_MAX-1)) + break; + data->rear_speed_rpm[index]=keyin; + case FAN1_PRESENT: + case FAN2_PRESENT: + case FAN3_PRESENT: + case FAN4_PRESENT: + case FAN5_PRESENT: + case FAN6_PRESENT: + status = kstrtol(buf, STRING_TO_DEC_VALUE, &keyin); + if (status) + break; + index=fan_array_index_get(attr->index); + if(index < 0 || index > (FAN_NUM_MAX-1)) + break; + if(keyin < 0 || keyin > 1) + break; + data->present[index]=keyin; + break; + case FAN1_FAULT: + case FAN2_FAULT: + case FAN3_FAULT: + case FAN4_FAULT: + case FAN5_FAULT: + case FAN6_FAULT: + status = kstrtol(buf, STRING_TO_DEC_VALUE, &keyin); + if (status) + break; + index=fan_array_index_get(attr->index); + if(index < 0 || index > (FAN_NUM_MAX-1)) + break; + if(keyin < 0 || keyin > 1) + break; + data->fault[index]=keyin; + break; + case FAN1_DIRECTION: + case FAN2_DIRECTION: + case FAN3_DIRECTION: + case FAN4_DIRECTION: + case FAN5_DIRECTION: + case FAN6_DIRECTION: + status = kstrtol(buf, STRING_TO_DEC_VALUE, &keyin); + if (status) + break; + index=fan_array_index_get(attr->index); + if(index < 0 || index > (FAN_NUM_MAX-1)) + break; + if(keyin < 0 || keyin > 1) + break; + data->direction[index]=keyin; + break; + default: + break; + } + mutex_unlock(&data->update_lock); + return size; +} +static ssize_t fan_value_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_fan_data *data = i2c_get_clientdata(client); + int status = -EINVAL; + int index=0; + //printk("ffan_value_show\n"); + //printk("attr->index=%d\n", attr->index); + mutex_lock(&data->update_lock); + switch (attr->index) + { + case FAN_DUTY_CYCLE_PERCENTAGE: + status = sprintf(buf, "%u\n", data->duty_cycle); + break; + case FAN1_FRONT_SPEED_RPM: + case FAN2_FRONT_SPEED_RPM: + case FAN3_FRONT_SPEED_RPM: + case FAN4_FRONT_SPEED_RPM: + case FAN5_FRONT_SPEED_RPM: + case FAN6_FRONT_SPEED_RPM: + index=fan_array_index_get(attr->index); + if(index < 0 || index > (FAN_NUM_MAX-1)) + break; + status = sprintf(buf, "%u\n", data->front_speed_rpm[index]); + break; + case FAN1_REAR_SPEED_RPM: + case FAN2_REAR_SPEED_RPM: + case FAN3_REAR_SPEED_RPM: + case FAN4_REAR_SPEED_RPM: + case FAN5_REAR_SPEED_RPM: + case FAN6_REAR_SPEED_RPM: + index=fan_array_index_get(attr->index); + if(index < 0 || index > (FAN_NUM_MAX-1)) + break; + status = sprintf(buf, "%u\n", data->rear_speed_rpm[index]); + break; + case FAN1_PRESENT: + case FAN2_PRESENT: + case FAN3_PRESENT: + case FAN4_PRESENT: + case FAN5_PRESENT: + case FAN6_PRESENT: + index=fan_array_index_get(attr->index); + if(index < 0 || index > (FAN_NUM_MAX-1)) + break; + status = sprintf(buf, "%d\n", data->present[index]); + break; + case FAN1_FAULT: + case FAN2_FAULT: + case FAN3_FAULT: + case FAN4_FAULT: + case FAN5_FAULT: + case FAN6_FAULT: + index=fan_array_index_get(attr->index); + if(index < 0 || index > (FAN_NUM_MAX-1)) + break; + status = sprintf(buf, "%d\n", is_fan_fault(data, index)); + break; + case FAN1_DIRECTION: + case FAN2_DIRECTION: + case FAN3_DIRECTION: + case FAN4_DIRECTION: + case FAN5_DIRECTION: + case FAN6_DIRECTION: + index=fan_array_index_get(attr->index);; + if(index < 0 || index > (FAN_NUM_MAX-1)) + break; + status = sprintf(buf, "%d\n", data->direction[index]); + break; + default: + break; + } + mutex_unlock(&data->update_lock); + return status; +} + +static const struct attribute_group as7716_32xb_fan_group = { + .attrs = as7716_32xb_fan_attributes, +}; + +static struct as7716_32xb_fan_data *as7716_32xb_fan_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_fan_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || + !data->valid) { + int i; + + dev_dbg(&client->dev, "Starting as7716_32xb_fan update\n"); + data->valid = 0; + + /* Update fan data + */ + for (i = 0; i < ARRAY_SIZE(data->reg_val); i++) { + int status = as7716_32xb_fan_read_value(client, fan_reg[i]); + + if (status < 0) { + data->valid = 0; + mutex_unlock(&data->update_lock); + dev_dbg(&client->dev, "reg %d, err %d\n", fan_reg[i], status); + return data; + } + else { + data->reg_val[i] = status; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int as7716_32xb_fan_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7716_32xb_fan_data *data; + int status; + + data = kzalloc(sizeof(struct as7716_32xb_fan_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, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7716_32xb_fan_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: fan '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_fan_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7716_32xb_fan_remove(struct i2c_client *client) +{ + struct as7716_32xb_fan_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_fan_group); + + return 0; +} + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x66, I2C_CLIENT_END }; + +static const struct i2c_device_id as7716_32xb_fan_id[] = { + { "as7716_32xb_fan", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7716_32xb_fan_id); + +static struct i2c_driver as7716_32xb_fan_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .probe = as7716_32xb_fan_probe, + .remove = as7716_32xb_fan_remove, + .id_table = as7716_32xb_fan_id, + .address_list = normal_i2c, +}; + +static int __init as7716_32xb_fan_init(void) +{ + return i2c_add_driver(&as7716_32xb_fan_driver); +} + +static void __exit as7716_32xb_fan_exit(void) +{ + i2c_del_driver(&as7716_32xb_fan_driver); +} + +module_init(as7716_32xb_fan_init); +module_exit(as7716_32xb_fan_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7716_32xb_fan driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_leds.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_leds.c new file mode 100755 index 000000000000..2fa3fd722cb1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_leds.c @@ -0,0 +1,443 @@ +/* + * A LED driver for the as7716_32xb_led + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * 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. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern void led_classdev_unregister(struct led_classdev *led_cdev); +extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev); +extern void led_classdev_resume(struct led_classdev *led_cdev); +extern void led_classdev_suspend(struct led_classdev *led_cdev); + +#define DRVNAME "as7716_32x_led" + +struct as7716_32x_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[1]; /* only 1 register*/ +}; + +static struct as7716_32x_led_data *ledctl = NULL; + +/* LED related data + */ + +#define LED_CNTRLER_I2C_ADDRESS (0x60) + +#define LED_TYPE_DIAG_REG_MASK (0x3) +#define LED_MODE_DIAG_GREEN_VALUE (0x02) +#define LED_MODE_DIAG_RED_VALUE (0x01) +#define LED_MODE_DIAG_AMBER_VALUE (0x00) /*It's yellow actually. Green+Red=Yellow*/ +#define LED_MODE_DIAG_OFF_VALUE (0x03) + + +#define LED_TYPE_LOC_REG_MASK (0x80) +#define LED_MODE_LOC_ON_VALUE (0) +#define LED_MODE_LOC_OFF_VALUE (0x80) + +enum led_type { + LED_TYPE_DIAG, + LED_TYPE_LOC, + LED_TYPE_FAN, + LED_TYPE_PSU1, + LED_TYPE_PSU2 +}; + +struct led_reg { + u32 types; + u8 reg_addr; +}; + +static const struct led_reg led_reg_map[] = { + {(1<update_lock); + + if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) + || !ledctl->valid) { + int i; + + dev_dbg(&ledctl->pdev->dev, "Starting as7716_32x_led update\n"); + + /* Update LED data + */ + for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { + int status = as7716_32x_led_read_value(led_reg_map[i].reg_addr); + + if (status < 0) { + ledctl->valid = 0; + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg_map[i].reg_addr, status); + goto exit; + } + else + { + ledctl->reg_val[i] = status; + } + } + + ledctl->last_updated = jiffies; + ledctl->valid = 1; + } + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void as7716_32x_led_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode, + enum led_type type) +{ + int reg_val; + u8 reg ; + mutex_lock(&ledctl->update_lock); + + if( !getLedReg(type, ®)) + { + dev_dbg(&ledctl->pdev->dev, "Not match item for %d.\n", type); + } + + reg_val = as7716_32x_led_read_value(reg); + + if (reg_val < 0) { + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", reg, reg_val); + goto exit; + } + reg_val = led_light_mode_to_reg_val(type, led_light_mode, reg_val); + as7716_32x_led_write_value(reg, reg_val); + + /* to prevent the slow-update issue */ + ledctl->valid = 0; + +exit: + mutex_unlock(&ledctl->update_lock); +} + + +static void as7716_32x_led_diag_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + as7716_32x_led_set(led_cdev, led_light_mode, LED_TYPE_DIAG); +} + +static enum led_brightness as7716_32x_led_diag_get(struct led_classdev *cdev) +{ + as7716_32x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); +} + +static void as7716_32x_led_loc_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + as7716_32x_led_set(led_cdev, led_light_mode, LED_TYPE_LOC); +} + +static enum led_brightness as7716_32x_led_loc_get(struct led_classdev *cdev) +{ + as7716_32x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); +} + +static void as7716_32x_led_auto_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ +} + +static enum led_brightness as7716_32x_led_auto_get(struct led_classdev *cdev) +{ + return LED_MODE_AUTO; +} + +static struct led_classdev as7716_32x_leds[] = { + [LED_TYPE_DIAG] = { + .name = "as7716_32x_led::diag", + .default_trigger = "unused", + .brightness_set = as7716_32x_led_diag_set, + .brightness_get = as7716_32x_led_diag_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_RED, + }, + [LED_TYPE_LOC] = { + .name = "as7716_32x_led::loc", + .default_trigger = "unused", + .brightness_set = as7716_32x_led_loc_set, + .brightness_get = as7716_32x_led_loc_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_BLUE, + }, + [LED_TYPE_FAN] = { + .name = "as7716_32x_led::fan", + .default_trigger = "unused", + .brightness_set = as7716_32x_led_auto_set, + .brightness_get = as7716_32x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU1] = { + .name = "as7716_32x_led::psu1", + .default_trigger = "unused", + .brightness_set = as7716_32x_led_auto_set, + .brightness_get = as7716_32x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU2] = { + .name = "as7716_32x_led::psu2", + .default_trigger = "unused", + .brightness_set = as7716_32x_led_auto_set, + .brightness_get = as7716_32x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, +}; + +static int as7716_32x_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(as7716_32x_leds); i++) { + led_classdev_suspend(&as7716_32x_leds[i]); + } + + return 0; +} + +static int as7716_32x_led_resume(struct platform_device *dev) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(as7716_32x_leds); i++) { + led_classdev_resume(&as7716_32x_leds[i]); + } + + return 0; +} + +static int as7716_32x_led_probe(struct platform_device *pdev) +{ + int ret, i; + + printk("as7716_32x_led_probe 1\n"); + for (i = 0; i < ARRAY_SIZE(as7716_32x_leds); i++) { + ret = led_classdev_register(&pdev->dev, &as7716_32x_leds[i]); + + if (ret < 0) + { + printk("ret < 0, i=%d\n", i); + break; + } + } + printk("as7716_32x_led_probe 2\n"); + /* Check if all LEDs were successfully registered */ + if (i != ARRAY_SIZE(as7716_32x_leds)){ + int j; + + /* only unregister the LEDs that were successfully registered */ + for (j = 0; j < i; j++) { + led_classdev_unregister(&as7716_32x_leds[i]); + } + } + + printk("as7716_32x_led_probe 3\n"); + return 0; +} + +static int as7716_32x_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(as7716_32x_leds); i++) { + led_classdev_unregister(&as7716_32x_leds[i]); + } + + return 0; +} + +static struct platform_driver as7716_32x_led_driver = { + .probe = as7716_32x_led_probe, + .remove = as7716_32x_led_remove, + .suspend = as7716_32x_led_suspend, + .resume = as7716_32x_led_resume, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init as7716_32x_led_init(void) +{ + int ret; + + printk("leds:1\n"); + ret = platform_driver_register(&as7716_32x_led_driver); + if (ret < 0) { + printk("leds:fail platform_driver_register\n"); + goto exit; + } + printk("leds:2\n"); + ledctl = kzalloc(sizeof(struct as7716_32x_led_data), GFP_KERNEL); + if (!ledctl) { + ret = -ENOMEM; + platform_driver_unregister(&as7716_32x_led_driver); + goto exit; + } + + mutex_init(&ledctl->update_lock); + printk("leds:3\n"); + ledctl->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(ledctl->pdev)) { + printk("leds:fail platform_device_register_simple\n"); + ret = PTR_ERR(ledctl->pdev); + platform_driver_unregister(&as7716_32x_led_driver); + kfree(ledctl); + goto exit; + } + +exit: + return 0; +} + +static void __exit as7716_32x_led_exit(void) +{ + platform_device_unregister(ledctl->pdev); + platform_driver_unregister(&as7716_32x_led_driver); + kfree(ledctl); +} + +module_init(as7716_32x_led_init); +module_exit(as7716_32x_led_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7716_32x_led driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_oom.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_oom.c new file mode 100755 index 000000000000..5e88d9efad5d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_oom.c @@ -0,0 +1,267 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STRING_TO_DEC_VALUE 10 +#define EEPROM_DATA_SIZE 512 + + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; +#define MAX_PORT_NAME_LEN 20 +/* Each client has this additional data + */ +struct as7716_32xb_oom_data { + struct device *hwmon_dev; + struct mutex lock; + u8 index; + unsigned char eeprom[EEPROM_DATA_SIZE]; + char port_name[MAX_PORT_NAME_LEN]; + +}; + + + +enum as7716_32xb_oom_sysfs_attributes { + TEMP1_INPUT, + TEMP1_MAX_HYST, + TEMP1_MAX +}; + +/* sysfs attributes for hwmon + */ + +static ssize_t oom_info_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t oom_info_show(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_port_name(struct device *dev, + struct device_attribute *dattr, char *buf); +static ssize_t set_port_name(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static SENSOR_DEVICE_ATTR(eeprom, S_IWUSR|S_IRUGO, oom_info_show, oom_info_store, 0); +static SENSOR_DEVICE_ATTR(port_name, S_IRUGO | S_IWUSR, show_port_name, set_port_name, 1); + + +static struct attribute *as7716_32xb_oom_attributes[] = { + &sensor_dev_attr_eeprom.dev_attr.attr, + &sensor_dev_attr_port_name.dev_attr.attr, + NULL +}; + + +static ssize_t oom_info_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_oom_data *data = i2c_get_clientdata(client); + int i; + + mutex_lock(&data->lock); + memcpy(buf, data->eeprom, EEPROM_DATA_SIZE); + //for(i=0; i < EEPROM_DATA_SIZE ; i++) + //{ + // buf[i]=data->eeprom[i]; + // printk("buf[%d]=0x%x ",i, buf[i]); + //} + //status = EEPROM_DATA_SIZE+1; + + + memcpy(buf, data->eeprom, 256); + mutex_unlock(&data->lock); + + return 256; +} + +static ssize_t oom_info_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t size) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_oom_data *data = i2c_get_clientdata(client); + int i=0, j=0, k=0; + unsigned char str[3]; + unsigned int val; + + // printk("strlen(buf)=%d\n",strlen(buf)); + k=0; + mutex_lock(&data->lock); + memset(data->eeprom, 0xFF, EEPROM_DATA_SIZE); + memset(str, 0x0, 3); + if(strlen(buf) >= 256 ) + { + for(i=0; i < strlen(buf) ; i++) + { + // printk("i=%d ", i); + for(j=0;j<2; j++) + { + str[j]=buf[i+j]; + } + sscanf(str, "%x", &val); + //printk("str=%s val=0x%x ", str, val); + i=j+i-1; + if(k>=EEPROM_DATA_SIZE) + { + break; + } + data->eeprom[k]=(unsigned char)val; + //printk("data->eeprom[%d]=0x%x\n",k, data->eeprom[k]); + k++; + } + } + //printk("buf=\n"); + //for(i=0; i < strlen(buf) ; i++) + //{ + // printk("%c%c ", buf[i], buf[i+1]); + // if((i % 31)==0) + // printk("\n"); + //} + //printk("\n"); + + + mutex_unlock(&data->lock); + return size; + +} + +static ssize_t show_port_name(struct device *dev, + struct device_attribute *dattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_oom_data *data = i2c_get_clientdata(client); + ssize_t count; + + mutex_lock(&data->lock); + count = sprintf(buf, "%s\n", data->port_name); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t set_port_name(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_oom_data *data = i2c_get_clientdata(client); + char port_name[MAX_PORT_NAME_LEN]; + + /* no checking, this value is not used except by show_port_name */ + + if (sscanf(buf, "%19s", port_name) != 1) + return -EINVAL; + + mutex_lock(&data->lock); + strcpy(data->port_name, port_name); + mutex_unlock(&data->lock); + + return count; +} + + +static const struct attribute_group as7716_32xb_oom_group = { + .attrs = as7716_32xb_oom_attributes, +}; + +static int as7716_32xb_oom_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7716_32xb_oom_data *data; + int status; + + data = kzalloc(sizeof(struct as7716_32xb_oom_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + i2c_set_clientdata(client, data); + data->index = dev_id->driver_data; + mutex_init(&data->lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7716_32xb_oom_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: oom '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_oom_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7716_32xb_oom_remove(struct i2c_client *client) +{ + struct as7716_32xb_oom_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_oom_group); + kfree(data); + + return 0; +} + + +static const struct i2c_device_id as7716_32xb_oom_id[] = { + { "as7716_32xb_oom", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7716_32xb_oom_id); + +static struct i2c_driver as7716_32xb_oom_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7716_32xb_oom", + }, + .probe = as7716_32xb_oom_probe, + .remove = as7716_32xb_oom_remove, + .id_table = as7716_32xb_oom_id, + .address_list = normal_i2c, +}; + + + + + + +static int __init as7716_32xb_oom_init(void) +{ + return i2c_add_driver(&as7716_32xb_oom_driver); +} + +static void __exit as7716_32xb_oom_exit(void) +{ + i2c_del_driver(&as7716_32xb_oom_driver); +} + +module_init(as7716_32xb_oom_init); +module_exit(as7716_32xb_oom_exit); + +MODULE_AUTHOR("Jostar yang "); +MODULE_DESCRIPTION("as7716_32xb_oom driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_pmbus.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_pmbus.c new file mode 100755 index 000000000000..3f4dba90d1aa --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_pmbus.c @@ -0,0 +1,444 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STRING_TO_DEC_VALUE 10 + + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as7716_32xb_pmbus_data { + struct device *hwmon_dev; + struct mutex update_lock; + u8 index; + u8 capability; + u16 status_word; + u8 fan_fault; + u8 over_temp; + u16 v_out; + u16 i_out; + u16 p_out; + u16 temp; + u16 fan_speed; + u16 fan_duty_cycle; + u8 fan_dir[4]; + u8 pmbus_revision; + u8 mfr_id[10]; + u8 mfr_model[10]; + u8 mfr_revision[3]; + u16 mfr_vin_min; + u16 mfr_vin_max; + u16 mfr_iin_max; + u16 mfr_iout_max; + u16 mfr_pin_max; + u16 mfr_pout_max; + u16 mfr_vout_min; + u16 mfr_vout_max; + u16 power_on; + u16 temp_fault; + u16 power_good; +}; + + + +enum as7716_32xb_pmbus_sysfs_attributes { + PSU_POWER_ON = 0, + PSU_TEMP_FAULT, + PSU_POWER_GOOD, + PSU_FAN1_FAULT, + PSU_FAN_DIRECTION, + PSU_OVER_TEMP, + PSU_V_OUT, + PSU_I_OUT, + PSU_P_OUT, + PSU_P_OUT_UV, /*In Unit of microVolt, instead of mini.*/ + PSU_TEMP1_INPUT, + PSU_FAN1_SPEED, + PSU_FAN1_DUTY_CYCLE, + PSU_PMBUS_REVISION, + PSU_MFR_ID, + PSU_MFR_MODEL, + PSU_MFR_REVISION, + PSU_MFR_VIN_MIN, + PSU_MFR_VIN_MAX, + PSU_MFR_VOUT_MIN, + PSU_MFR_VOUT_MAX, + PSU_MFR_IIN_MAX, + PSU_MFR_IOUT_MAX, + PSU_MFR_PIN_MAX, + PSU_MFR_POUT_MAX +}; + +/* sysfs attributes for hwmon + */ +static ssize_t pmbus_info_show(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t pmbus_info_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static SENSOR_DEVICE_ATTR(psu_power_on, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_POWER_ON); +static SENSOR_DEVICE_ATTR(psu_temp_fault, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_TEMP_FAULT); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_POWER_GOOD); +static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_FAN1_FAULT); +static SENSOR_DEVICE_ATTR(psu_over_temp, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_OVER_TEMP); +static SENSOR_DEVICE_ATTR(psu_v_out, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(psu_i_out, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(psu_p_out, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_P_OUT); +static SENSOR_DEVICE_ATTR(psu_temp1_input,S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm,S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_FAN1_SPEED); +static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IWUSR | S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_FAN1_DUTY_CYCLE); +static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_FAN_DIRECTION); +static SENSOR_DEVICE_ATTR(psu_pmbus_revision,S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_PMBUS_REVISION); +static SENSOR_DEVICE_ATTR(psu_mfr_id, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_MFR_ID); +static SENSOR_DEVICE_ATTR(psu_mfr_model, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_MFR_MODEL); +static SENSOR_DEVICE_ATTR(psu_mfr_revision, S_IWUSR| S_IRUGO, pmbus_info_show,pmbus_info_store, PSU_MFR_REVISION); +static SENSOR_DEVICE_ATTR(psu_mfr_vin_min, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_MFR_VIN_MIN); +static SENSOR_DEVICE_ATTR(psu_mfr_vin_max, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_MFR_VIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_vout_min, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_MFR_VOUT_MIN); +static SENSOR_DEVICE_ATTR(psu_mfr_vout_max, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_MFR_VOUT_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_iin_max, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_MFR_IIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_iout_max, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_MFR_IOUT_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_pin_max, S_IWUSR|S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_MFR_PIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_pout_max, S_IWUSR| S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_MFR_POUT_MAX); + +/*Duplicate nodes for lm-sensors.*/ +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_P_OUT_UV); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_FAN1_SPEED); +static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, pmbus_info_show, pmbus_info_store, PSU_TEMP_FAULT); + + +static struct attribute *as7716_32xb_pmbus_attributes[] = { + &sensor_dev_attr_psu_power_on.dev_attr.attr, + &sensor_dev_attr_psu_temp_fault.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, + &sensor_dev_attr_psu_over_temp.dev_attr.attr, + &sensor_dev_attr_psu_v_out.dev_attr.attr, + &sensor_dev_attr_psu_i_out.dev_attr.attr, + &sensor_dev_attr_psu_p_out.dev_attr.attr, + &sensor_dev_attr_psu_temp1_input.dev_attr.attr, + &sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr, + &sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr, + &sensor_dev_attr_psu_fan_dir.dev_attr.attr, + &sensor_dev_attr_psu_pmbus_revision.dev_attr.attr, + &sensor_dev_attr_psu_mfr_id.dev_attr.attr, + &sensor_dev_attr_psu_mfr_model.dev_attr.attr, + &sensor_dev_attr_psu_mfr_revision.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vin_min.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_pout_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_iin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_pin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vout_min.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vout_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_iout_max.dev_attr.attr, + /*Duplicate nodes for lm-sensors.*/ + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + NULL +}; + + +static ssize_t pmbus_info_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_pmbus_data *data = i2c_get_clientdata(client); + int status = -EINVAL; + //printk("pmbus_info_show\n"); + printk("attr->index=%d\n", attr->index); + mutex_lock(&data->update_lock); + switch (attr->index) + { + case PSU_POWER_ON: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->power_on); + break; + case PSU_TEMP_FAULT: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->temp_fault); + break; + case PSU_POWER_GOOD: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->power_good); + break; + case PSU_FAN1_FAULT: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->fan_fault); + break; + case PSU_FAN_DIRECTION: + status=snprintf(buf, PAGE_SIZE-1, "%s\r\n", data->fan_dir); + break; + case PSU_OVER_TEMP: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->over_temp); + break; + case PSU_V_OUT: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->v_out); + break; + case PSU_I_OUT: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->i_out); + break; + case PSU_P_OUT: + printk("read PSU_P_OUT\n"); + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->p_out); + break; + case PSU_P_OUT_UV: + printk("read PSU_P_OUT_UV\n"); + status=snprintf(buf, PAGE_SIZE-1, "%ld\r\n", data->p_out * 1000000); + break; + case PSU_TEMP1_INPUT: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->temp); + break; + case PSU_FAN1_SPEED: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->fan_speed); + break; + case PSU_FAN1_DUTY_CYCLE: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->fan_duty_cycle); + break; + case PSU_PMBUS_REVISION: + break; + case PSU_MFR_ID: + status=snprintf(buf, PAGE_SIZE-1, "%s\r\n", data->mfr_id); + break; + case PSU_MFR_MODEL: + status=snprintf(buf, PAGE_SIZE-1, "%s\r\n", data->mfr_model); + break; + case PSU_MFR_REVISION: + status=snprintf(buf, PAGE_SIZE-1, "%s\r\n", data->mfr_revision); + break; + case PSU_MFR_VIN_MIN: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->mfr_vin_min); + break; + case PSU_MFR_VIN_MAX: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->mfr_vin_max); + break; + case PSU_MFR_VOUT_MIN: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->mfr_vin_max); + break; + case PSU_MFR_VOUT_MAX: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->mfr_vout_max); + break; + case PSU_MFR_IIN_MAX: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->mfr_iin_max); + break; + case PSU_MFR_IOUT_MAX: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->mfr_iout_max); + break; + case PSU_MFR_PIN_MAX: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->mfr_pin_max); + break; + case PSU_MFR_POUT_MAX: + status=snprintf(buf, PAGE_SIZE-1, "%d\r\n", data->mfr_pout_max); + break; + default : + break; + } + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t pmbus_info_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_pmbus_data *data = i2c_get_clientdata(client); + long keyin = 0; + int status = -EINVAL; + //printk("pmbus_info_store\n"); + //printk("attr->index=%d\n", attr->index); + mutex_lock(&data->update_lock); + status = kstrtol(buf, STRING_TO_DEC_VALUE, &keyin); + switch (attr->index) + { + case PSU_POWER_ON: + data->power_on=keyin; + break; + case PSU_TEMP_FAULT: + data->temp_fault=keyin; + break; + case PSU_POWER_GOOD: + data->power_good=keyin; + break; + case PSU_FAN1_FAULT: + break; + case PSU_FAN_DIRECTION: + memcpy(data->fan_dir, buf, sizeof(data->fan_dir)); + break; + case PSU_OVER_TEMP: + data->over_temp=keyin; + break; + case PSU_V_OUT: + data->v_out=keyin; + break; + case PSU_I_OUT: + data->i_out=keyin; + break; + case PSU_P_OUT: + printk("data->p_out=%d\n", data->p_out); + data->p_out=keyin; + break; + case PSU_P_OUT_UV: + //multiplier = 1000000; /*For lm-sensors, unit is micro-Volt.*/ + + break; + case PSU_TEMP1_INPUT: + data->temp=keyin; + break; + case PSU_FAN1_SPEED: + data->fan_speed=keyin; + break; + case PSU_FAN1_DUTY_CYCLE: + break; + case PSU_PMBUS_REVISION: + data->pmbus_revision=keyin; + break; + case PSU_MFR_ID: + memcpy(data->mfr_id, buf, sizeof(data->mfr_id)); + break; + case PSU_MFR_MODEL: + memcpy(data->mfr_model, buf, sizeof(data->mfr_model)); + break; + case PSU_MFR_REVISION: + memcpy(data->mfr_revision, buf, sizeof(data->mfr_revision)); + break; + case PSU_MFR_VIN_MIN: + data->mfr_vin_min=keyin; + break; + case PSU_MFR_VIN_MAX: + data->mfr_vin_max=keyin; + break; + case PSU_MFR_VOUT_MIN: + data->mfr_vout_min=keyin; + break; + case PSU_MFR_VOUT_MAX: + data->mfr_vout_max=keyin; + break; + case PSU_MFR_IIN_MAX: + data->mfr_iin_max=keyin; + break; + case PSU_MFR_IOUT_MAX: + data->mfr_iout_max=keyin; + break; + case PSU_MFR_PIN_MAX: + data->mfr_pin_max=keyin; + break; + case PSU_MFR_POUT_MAX: + data->mfr_pout_max=keyin; + break; + default : + break; + } + mutex_unlock(&data->update_lock); + return count; +} + + +static const struct attribute_group as7716_32xb_pmbus_group = { + .attrs = as7716_32xb_pmbus_attributes, +}; + +static int as7716_32xb_pmbus_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7716_32xb_pmbus_data *data; + int status; + + data = kzalloc(sizeof(struct as7716_32xb_pmbus_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + i2c_set_clientdata(client, data); + data->index = dev_id->driver_data; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7716_32xb_pmbus_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: pmbus '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_pmbus_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7716_32xb_pmbus_remove(struct i2c_client *client) +{ + struct as7716_32xb_pmbus_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_pmbus_group); + kfree(data); + + return 0; +} + + +static const struct i2c_device_id as7716_32xb_pmbus_id[] = { + { "as7716_32xb_pmbus", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7716_32xb_pmbus_id); + +static struct i2c_driver as7716_32xb_pmbus_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7716_32xb_pmbus", + }, + .probe = as7716_32xb_pmbus_probe, + .remove = as7716_32xb_pmbus_remove, + .id_table = as7716_32xb_pmbus_id, + .address_list = normal_i2c, +}; + + + + + + +static int __init as7716_32xb_pmbus_init(void) +{ + return i2c_add_driver(&as7716_32xb_pmbus_driver); +} + +static void __exit as7716_32xb_pmbus_exit(void) +{ + i2c_del_driver(&as7716_32xb_pmbus_driver); +} + +module_init(as7716_32xb_pmbus_init); +module_exit(as7716_32xb_pmbus_exit); + +MODULE_AUTHOR("Jostar yang "); +MODULE_DESCRIPTION("as7716_32xb_pmbus driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_psu.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_psu.c new file mode 100755 index 000000000000..01756e8596eb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_psu.c @@ -0,0 +1,355 @@ +/* + * An hwmon driver for accton as7716_32xbb Power Module + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_MODEL_NAME 16 +#define DC12V_FAN_DIR_OFFSET 0x34 +#define DC12V_FAN_DIR_LEN 3 +#define STRING_TO_DEC_VALUE 10 + +//static ssize_t show_string(struct device *dev, struct device_attribute *da, char *buf); +static int as7716_32xb_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); +extern int as7716_32xb_cpld_read (unsigned short cpld_addr, u8 reg); + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as7716_32xb_psu_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + u8 status; /* Status(present/power_good) register read from CPLD */ + u8 present; + u8 power_good; + char model_name[MAX_MODEL_NAME+1]; /* Model name, read from eeprom */ + char fan_dir[DC12V_FAN_DIR_LEN+1]; /* DC12V fan direction */ +}; + +enum as7716_32xb_psu_sysfs_attributes { + PSU_PRESENT, + PSU_MODEL_NAME, + PSU_POWER_GOOD, + PSU_FAN_DIR /* For DC12V only */ +}; + +/* sysfs attributes for hwmon + */ +static ssize_t psu_info_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t psu_info_show(struct device *dev, struct device_attribute *da, + char *buf); +static SENSOR_DEVICE_ATTR(psu_present, S_IWUSR|S_IRUGO, psu_info_show, psu_info_store, PSU_PRESENT); +static SENSOR_DEVICE_ATTR(psu_model_name, S_IWUSR|S_IRUGO, psu_info_show, psu_info_store, PSU_MODEL_NAME); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IWUSR|S_IRUGO, psu_info_show, psu_info_store, PSU_POWER_GOOD); +static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IWUSR|S_IRUGO, psu_info_show, psu_info_store, PSU_FAN_DIR); + + +static struct attribute *as7716_32xb_psu_attributes[] = { + &sensor_dev_attr_psu_present.dev_attr.attr, + &sensor_dev_attr_psu_model_name.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + &sensor_dev_attr_psu_fan_dir.dev_attr.attr, + NULL +}; + +static ssize_t psu_info_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_psu_data *data = i2c_get_clientdata(client); + int status = -EINVAL; + //printk("psu_info_show\n"); + // printk("attr->index=%d\n", attr->index); + mutex_lock(&data->update_lock); + switch (attr->index) + { + case PSU_PRESENT: + //printk("data->present=%d\n",data->present); + status = snprintf(buf, PAGE_SIZE - 1, "%d\r\n", data->present); + break; + case PSU_MODEL_NAME: + //printk("data->model_name=%s\n",data->model_name); + status = snprintf(buf, PAGE_SIZE - 1, "%s\r\n", data->model_name); + break; + case PSU_POWER_GOOD: + // printk("data->present=%d\n",data->power_good); + status = snprintf(buf, PAGE_SIZE - 1, "%d\r\n", data->power_good); + break; + case PSU_FAN_DIR: + //printk("data->fan_dir=%s\n",data->fan_dir); + status = snprintf(buf, PAGE_SIZE - 1, "%s\r\n", data->fan_dir); + break; + default : + break; + } + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t psu_info_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_psu_data *data = i2c_get_clientdata(client); + long keyin = 0; + int status = -EINVAL; + //printk("psu_info_store\n"); + //printk("attr->index=%d\n", attr->index); + mutex_lock(&data->update_lock); + switch (attr->index) + { + case PSU_PRESENT: + status = kstrtol(buf, STRING_TO_DEC_VALUE, &keyin); + if (status) + goto fail_exit; + if (keyin > 1 || keyin < 0) + goto fail_exit; + data->present=keyin; + break; + case PSU_MODEL_NAME: + memcpy(data->model_name, buf, MAX_MODEL_NAME); + break; + case PSU_POWER_GOOD: + status = kstrtol(buf, STRING_TO_DEC_VALUE, &keyin); + if (status) + goto fail_exit; + if (keyin > 1 || keyin < 0) + goto fail_exit; + data->power_good=keyin; + break; + case PSU_FAN_DIR: + memcpy(data->fan_dir, buf, DC12V_FAN_DIR_LEN); + break; + default : + goto fail_exit; + } + mutex_unlock(&data->update_lock); + return count; + +fail_exit: + mutex_unlock(&data->update_lock); + return -EINVAL; +} + +static const struct attribute_group as7716_32xb_psu_group = { + .attrs = as7716_32xb_psu_attributes, +}; + +static int as7716_32xb_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7716_32xb_psu_data *data; + int status; + + data = kzalloc(sizeof(struct as7716_32xb_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7716_32xb_psu_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7716_32xb_psu_remove(struct i2c_client *client) +{ + struct as7716_32xb_psu_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_psu_group); + kfree(data); + + return 0; +} + +enum psu_index +{ + as7716_32xb_psu1, + as7716_32xb_psu2 +}; + +static const struct i2c_device_id as7716_32xb_psu_id[] = { + { "as7716_32xb_psu1", as7716_32xb_psu1 }, + { "as7716_32xb_psu2", as7716_32xb_psu2 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7716_32xb_psu_id); + +static struct i2c_driver as7716_32xb_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7716_32xb_psu", + }, + .probe = as7716_32xb_psu_probe, + .remove = as7716_32xb_psu_remove, + .id_table = as7716_32xb_psu_id, + .address_list = normal_i2c, +}; + +static int as7716_32xb_psu_read_block(struct i2c_client *client, u8 command, u8 *data, + int data_len) +{ + int result = 0; + int retry_count = 5; + + while (retry_count) { + retry_count--; + + result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); + + if (unlikely(result < 0)) { + msleep(10); + continue; + } + + if (unlikely(result != data_len)) { + result = -EIO; + msleep(10); + continue; + } + + result = 0; + break; + } + + return result; +} + +enum psu_type { + PSU_TYPE_AC_110V, + PSU_TYPE_DC_48V, + PSU_TYPE_DC_12V +}; + +struct model_name_info { + enum psu_type type; + u8 offset; + u8 length; + char* model_name; +}; + +struct model_name_info models[] = { +{PSU_TYPE_AC_110V, 0x20, 8, "YM-2651Y"}, +{PSU_TYPE_DC_48V, 0x20, 8, "YM-2651V"}, +{PSU_TYPE_DC_12V, 0x00, 11, "PSU-12V-750"}, +}; + +static int as7716_32xb_psu_model_name_get(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_psu_data *data = i2c_get_clientdata(client); + int i, status; + + for (i = 0; i < ARRAY_SIZE(models); i++) { + memset(data->model_name, 0, sizeof(data->model_name)); + + status = as7716_32xb_psu_read_block(client, models[i].offset, + data->model_name, models[i].length); + if (status < 0) { + data->model_name[0] = '\0'; + dev_dbg(&client->dev, "unable to read model name from (0x%x) offset(0x%x)\n", + client->addr, models[i].offset); + return status; + } + else { + data->model_name[models[i].length] = '\0'; + } + + /* Determine if the model name is known, if not, read next index + */ + if (strncmp(data->model_name, models[i].model_name, models[i].length) == 0) { + return 0; + } + else { + data->model_name[0] = '\0'; + } + } + + return -ENODATA; +} + +static int __init as7716_32xb_psu_init(void) +{ + return i2c_add_driver(&as7716_32xb_psu_driver); +} + +static void __exit as7716_32xb_psu_exit(void) +{ + i2c_del_driver(&as7716_32xb_psu_driver); +} + +module_init(as7716_32xb_psu_init); +module_exit(as7716_32xb_psu_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7716_32xb_psu driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_sfp.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_sfp.c new file mode 100755 index 000000000000..9c7a05632154 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_sfp.c @@ -0,0 +1,365 @@ +/* + * An hwmon driver for accton as7716_32x sfp + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BIT_INDEX(i) (1UL << (i)) +#define I2C_ADDR_CPLD1 0x60 +#define I2C_ADDR_CPLD2 0x62 +#define I2C_ADDR_CPLD3 0x64 +#define CPLD1_OFFSET_QSFP_PRESET1 0x30 +#define CPLD1_OFFSET_QSFP_PRESET2 0x31 +#define CPLD1_OFFSET_QSFP_PRESET3 0x32 +#define CPLD1_OFFSET_QSFP_PRESET4 0x33 + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as7716_32x_sfp_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + int port; /* Front port index */ + char eeprom[256]; /* eeprom data */ + u32 is_present; /* present status */ +}; + +static struct as7716_32x_sfp_data *as7716_32x_sfp_update_device(struct device *dev); +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_present(struct device *dev, struct device_attribute *da,char *buf); +static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, char *buf); +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +enum as7716_32x_sfp_sysfs_attributes { + SFP_PORT_NUMBER, + SFP_IS_PRESENT, + SFP_IS_PRESENT_ALL, + SFP_EEPROM +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, SFP_PORT_NUMBER); +static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_present, NULL, SFP_IS_PRESENT); +static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_present, NULL, SFP_IS_PRESENT_ALL); +static SENSOR_DEVICE_ATTR(sfp_eeprom, S_IRUGO, show_eeprom, NULL, SFP_EEPROM); + +static struct attribute *as7716_32x_sfp_attributes[] = { + &sensor_dev_attr_sfp_port_number.dev_attr.attr, + &sensor_dev_attr_sfp_is_present.dev_attr.attr, + &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, + &sensor_dev_attr_sfp_eeprom.dev_attr.attr, + NULL +}; + +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_sfp_data *data = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", data->port+1); +} + +/* Error-check the CPLD read results. */ +#define VALIDATED_READ(_buf, _rv, _read_expr, _invert) \ +do { \ + _rv = (_read_expr); \ + if(_rv < 0) { \ + return sprintf(_buf, "READ ERROR\n"); \ + } \ + if(_invert) { \ + _rv = ~_rv; \ + } \ + _rv &= 0xFF; \ +} while(0) + +static ssize_t show_present(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + + if(attr->index == SFP_IS_PRESENT_ALL) { + int values[4]; + /* + * Report the SFP_PRESENCE status for all ports. + */ + + /* QSFP_PRESENT Ports 1-8 */ + //VALIDATED_READ(buf, values[0], accton_i2c_cpld_read(0x62, 0x9), 1); + VALIDATED_READ(buf, values[0], accton_i2c_cpld_read(I2C_ADDR_CPLD1, CPLD1_OFFSET_QSFP_PRESET1), 1); + /* QSFP_PRESENT Ports 9-16 */ + VALIDATED_READ(buf, values[1], accton_i2c_cpld_read(I2C_ADDR_CPLD1, CPLD1_OFFSET_QSFP_PRESET2), 1); + /* QSFP_PRESENT Ports 17-24 */ + VALIDATED_READ(buf, values[2], accton_i2c_cpld_read(I2C_ADDR_CPLD1, CPLD1_OFFSET_QSFP_PRESET3), 1); + /* QSFP_PRESENT Ports 25-32 */ + VALIDATED_READ(buf, values[3], accton_i2c_cpld_read(I2C_ADDR_CPLD1, CPLD1_OFFSET_QSFP_PRESET4), 1); + + /* Return values 1 -> 32 in order */ + return sprintf(buf, "%.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], values[3]); + } + else { /* SFP_IS_PRESENT */ + struct as7716_32x_sfp_data *data = as7716_32x_sfp_update_device(dev); + + if (!data->valid) { + printk("return -EIO\n"); + return -EIO; + } + + return sprintf(buf, "%d\n", data->is_present); + } +} + +static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as7716_32x_sfp_data *data = as7716_32x_sfp_update_device(dev); + + if (!data->valid) { + return 0; + } + + if (!data->is_present) { + return 0; + } + + memcpy(buf, data->eeprom, sizeof(data->eeprom)); + + return sizeof(data->eeprom); +} + +static const struct attribute_group as7716_32x_sfp_group = { + .attrs = as7716_32x_sfp_attributes, +}; + +static int as7716_32x_sfp_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7716_32x_sfp_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7716_32x_sfp_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + mutex_init(&data->update_lock); + data->port = dev_id->driver_data; + i2c_set_clientdata(client, data); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7716_32x_sfp_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: sfp '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7716_32x_sfp_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7716_32x_sfp_remove(struct i2c_client *client) +{ + struct as7716_32x_sfp_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7716_32x_sfp_group); + kfree(data); + + return 0; +} + +enum port_numbers { +as7716_32x_sfp1, as7716_32x_sfp2, as7716_32x_sfp3, as7716_32x_sfp4, +as7716_32x_sfp5, as7716_32x_sfp6, as7716_32x_sfp7, as7716_32x_sfp8, +as7716_32x_sfp9, as7716_32x_sfp10,as7716_32x_sfp11,as7716_32x_sfp12, +as7716_32x_sfp13,as7716_32x_sfp14,as7716_32x_sfp15,as7716_32x_sfp16, +as7716_32x_sfp17,as7716_32x_sfp18,as7716_32x_sfp19,as7716_32x_sfp20, +as7716_32x_sfp21,as7716_32x_sfp22,as7716_32x_sfp23,as7716_32x_sfp24, +as7716_32x_sfp25,as7716_32x_sfp26,as7716_32x_sfp27,as7716_32x_sfp28, +as7716_32x_sfp29,as7716_32x_sfp30,as7716_32x_sfp31,as7716_32x_sfp32 +}; + +static const struct i2c_device_id as7716_32x_sfp_id[] = { +{ "as7716_32x_sfp1", as7716_32x_sfp1 }, { "as7716_32x_sfp2", as7716_32x_sfp2 }, +{ "as7716_32x_sfp3", as7716_32x_sfp3 }, { "as7716_32x_sfp4", as7716_32x_sfp4 }, +{ "as7716_32x_sfp5", as7716_32x_sfp5 }, { "as7716_32x_sfp6", as7716_32x_sfp6 }, +{ "as7716_32x_sfp7", as7716_32x_sfp7 }, { "as7716_32x_sfp8", as7716_32x_sfp8 }, +{ "as7716_32x_sfp9", as7716_32x_sfp9 }, { "as7716_32x_sfp10", as7716_32x_sfp10 }, +{ "as7716_32x_sfp11", as7716_32x_sfp11 }, { "as7716_32x_sfp12", as7716_32x_sfp12 }, +{ "as7716_32x_sfp13", as7716_32x_sfp13 }, { "as7716_32x_sfp14", as7716_32x_sfp14 }, +{ "as7716_32x_sfp15", as7716_32x_sfp15 }, { "as7716_32x_sfp16", as7716_32x_sfp16 }, +{ "as7716_32x_sfp17", as7716_32x_sfp17 }, { "as7716_32x_sfp18", as7716_32x_sfp18 }, +{ "as7716_32x_sfp19", as7716_32x_sfp19 }, { "as7716_32x_sfp20", as7716_32x_sfp20 }, +{ "as7716_32x_sfp21", as7716_32x_sfp21 }, { "as7716_32x_sfp22", as7716_32x_sfp22 }, +{ "as7716_32x_sfp23", as7716_32x_sfp23 }, { "as7716_32x_sfp24", as7716_32x_sfp24 }, +{ "as7716_32x_sfp25", as7716_32x_sfp25 }, { "as7716_32x_sfp26", as7716_32x_sfp26 }, +{ "as7716_32x_sfp27", as7716_32x_sfp27 }, { "as7716_32x_sfp28", as7716_32x_sfp28 }, +{ "as7716_32x_sfp29", as7716_32x_sfp29 }, { "as7716_32x_sfp30", as7716_32x_sfp30 }, +{ "as7716_32x_sfp31", as7716_32x_sfp31 }, { "as7716_32x_sfp32", as7716_32x_sfp32 }, +{} +}; +MODULE_DEVICE_TABLE(i2c, as7716_32x_sfp_id); + +static struct i2c_driver as7716_32x_sfp_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7716_32x_sfp", + }, + .probe = as7716_32x_sfp_probe, + .remove = as7716_32x_sfp_remove, + .id_table = as7716_32x_sfp_id, + .address_list = normal_i2c, +}; + +static int as7716_32x_sfp_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; +} + +static struct as7716_32x_sfp_data *as7716_32x_sfp_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32x_sfp_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int status = -1; + int i = 0; + u8 cpld_reg = 0x30 + (data->port/8); + + data->valid = 0; + + /* Read present status of the specified port number */ + data->is_present = 0; + status = accton_i2c_cpld_read(I2C_ADDR_CPLD1, cpld_reg); + + if (status < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", I2C_ADDR_CPLD1, cpld_reg, status); + goto exit; + } + + data->is_present = (status & (1 << (data->port % 8))) ? 0 : 1; + printk("data->is_present=%d, data->port=%d, status=0x%x\n",data->is_present, data->port, status); + /* Read eeprom data based on port number */ + memset(data->eeprom, 0, sizeof(data->eeprom)); + + /* Check if the port is present */ + if (data->is_present) { + /* read eeprom */ + for (i = 0; i < sizeof(data->eeprom)/I2C_SMBUS_BLOCK_MAX; i++) { + status = as7716_32x_sfp_read_block(client, i*I2C_SMBUS_BLOCK_MAX, + data->eeprom+(i*I2C_SMBUS_BLOCK_MAX), + I2C_SMBUS_BLOCK_MAX); + if (status < 0) { + printk("unable to read eeprom from port(%d)\n", data->port); + dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", data->port); + goto exit; + } + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init as7716_32x_sfp_init(void) +{ + //extern int platform_accton_as7716_32x(void); + //if (!platform_accton_as7716_32x()) { +// return -ENODEV; + //} + + return i2c_add_driver(&as7716_32x_sfp_driver); +} + +static void __exit as7716_32x_sfp_exit(void) +{ + i2c_del_driver(&as7716_32x_sfp_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton as7716_32x_sfp driver"); +MODULE_LICENSE("GPL"); + +module_init(as7716_32x_sfp_init); +module_exit(as7716_32x_sfp_exit); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_sys.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_sys.c new file mode 100755 index 000000000000..f0a8125139cc --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_sys.c @@ -0,0 +1,243 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STRING_TO_DEC_VALUE 10 +#define EEPROM_DATA_SIZE 256 + + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; +#define MAX_PORT_NAME_LEN 20 +/* Each client has this additional data + */ +struct as7716_32xb_sys_data { + struct device *hwmon_dev; + struct mutex lock; + u8 index; + unsigned char eeprom[EEPROM_DATA_SIZE]; +}; + + +/* sysfs attributes for hwmon + */ + +static ssize_t sys_info_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t sys_info_show(struct device *dev, struct device_attribute *da, + char *buf); + +static SENSOR_DEVICE_ATTR(eeprom, S_IWUSR|S_IRUGO, sys_info_show, sys_info_store, 0); + + + +static struct attribute *as7716_32xb_sys_attributes[] = { + &sensor_dev_attr_eeprom.dev_attr.attr, + NULL +}; + + +static ssize_t sys_info_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + //struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_sys_data *data = i2c_get_clientdata(client); +// int status = -EINVAL; + int i; + + //printk("sys_info_show\n"); + // printk("attr->index=%d\n", attr->index); + mutex_lock(&data->lock); + //for(i=0; i<8; i++) + // printk("data->eeprom[%d]=0x%x ",i, data->eeprom[i]); + //printk("\n"); + memcpy(buf, data->eeprom, EEPROM_DATA_SIZE); + //for(i=0; i < EEPROM_DATA_SIZE ; i++) + //{ + // buf[i]=data->eeprom[i]; + // printk("buf[%d]=0x%x ",i, buf[i]); + //} + //status = EEPROM_DATA_SIZE+1; + + //printk("\n"); + //status = sprintf(buf, "%x", 0xA); + //data->eeprom[0]=0x0d; + //data->eeprom[1]=0x0; + //data->eeprom[2]=0x06; + // buf[3]=0xFF; + + // for(i=0; i< 16; i++) + // printk("buf[%d]=0x%x ",i, buf[i]); + //printk("\n"); + + memcpy(buf, data->eeprom, 256); + + + mutex_unlock(&data->lock); + + return 256; +} + +static ssize_t sys_info_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t size) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_sys_data *data = i2c_get_clientdata(client); + int i=0, j=0, k=0; + unsigned char str[3]; + unsigned int val; + + //printk("sys_info_store\n"); + //printk("attr->index=%d\n", attr->index); + //printk("buf[0]=0x%x, buf[1]=0x%x, buf[2]=0x%x, buf[3]=0x%x\n", buf[0], buf[1], buf[2], buf[3]); + + // printk("strlen(buf)=%d\n",strlen(buf)); + k=0; + mutex_lock(&data->lock); + memset(data->eeprom, 0xFF, EEPROM_DATA_SIZE); + memset(str, 0x0, 3); + if(strlen(buf) >= 256 ) + { + for(i=0; i < strlen(buf) ; i++) + { + // printk("i=%d ", i); + for(j=0;j<2; j++) + { + str[j]=buf[i+j]; + } + sscanf(str, "%x", &val); + // printk("str=%s val=0x%x ", str, val); + i=j+i-1; + if(k>=EEPROM_DATA_SIZE) + { + break; + } + data->eeprom[k]=(unsigned char)val; + // printk("data->eeprom[%d]=0x%x\n",k, data->eeprom[k]); + k++; + } + } + //printk("buf=\n"); + //for(i=0; i < strlen(buf) ; i++) + //{ + // printk("%c%c ", buf[i], buf[i+1]); + // if((i % 31)==0) + // printk("\n"); + //} + //printk("\n"); + + //printk("eeprom[0]=0x%x, eeprom[1]=0x%x, eeprom[2]=0x%x, eeprom[3]=0x%x\n", + // data->eeprom[0], data->eeprom[1], data->eeprom[2], data->eeprom[3]); + + mutex_unlock(&data->lock); + return size; + +} + +static const struct attribute_group as7716_32xb_sys_group = { + .attrs = as7716_32xb_sys_attributes, +}; + +static int as7716_32xb_sys_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7716_32xb_sys_data *data; + int status; + + data = kzalloc(sizeof(struct as7716_32xb_sys_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + i2c_set_clientdata(client, data); + data->index = dev_id->driver_data; + mutex_init(&data->lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7716_32xb_sys_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: sys '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_sys_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7716_32xb_sys_remove(struct i2c_client *client) +{ + struct as7716_32xb_sys_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_sys_group); + kfree(data); + + return 0; +} + + +static const struct i2c_device_id as7716_32xb_sys_id[] = { + { "as7716_32xb_sys", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7716_32xb_sys_id); + +static struct i2c_driver as7716_32xb_sys_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7716_32xb_sys", + }, + .probe = as7716_32xb_sys_probe, + .remove = as7716_32xb_sys_remove, + .id_table = as7716_32xb_sys_id, + .address_list = normal_i2c, +}; + + + + + + +static int __init as7716_32xb_sys_init(void) +{ + return i2c_add_driver(&as7716_32xb_sys_driver); +} + +static void __exit as7716_32xb_sys_exit(void) +{ + i2c_del_driver(&as7716_32xb_sys_driver); +} + +module_init(as7716_32xb_sys_init); +module_exit(as7716_32xb_sys_exit); + +MODULE_AUTHOR("Jostar yang "); +MODULE_DESCRIPTION("as7716_32xb_sys driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_thermal.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_thermal.c new file mode 100755 index 000000000000..d42341bb181a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_as7716_32xb_thermal.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STRING_TO_DEC_VALUE 10 +#define STRING_TO_HEX_VALUE 16 + +#define TEMP1_MAX_HYST_DEFAULT 75000 +#define TEMP1_MAX_DEFAULT 80000 +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as7716_32xb_thermal_data { + struct device *hwmon_dev; + struct mutex update_lock; + u8 index; + unsigned int temp1_input; + unsigned int temp1_max_hyst; + unsigned int temp1_max; +}; + + + +enum as7716_32xb_thermal_sysfs_attributes { + TEMP1_INPUT, + TEMP1_MAX_HYST, + TEMP1_MAX +}; + +/* sysfs attributes for hwmon + */ + +static ssize_t temp_info_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t temp_info_show(struct device *dev, struct device_attribute *da, + char *buf); +static SENSOR_DEVICE_ATTR(temp1_input, S_IWUSR|S_IRUGO, temp_info_show, temp_info_store, TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR|S_IRUGO, temp_info_show, temp_info_store, TEMP1_MAX_HYST); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR|S_IRUGO, temp_info_show, temp_info_store, TEMP1_MAX); + + +static struct attribute *as7716_32xb_thermal_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + NULL +}; + + +static ssize_t temp_info_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_thermal_data *data = i2c_get_clientdata(client); + int status = -EINVAL; + + mutex_lock(&data->update_lock); + switch (attr->index) + { + case TEMP1_INPUT: + status = snprintf(buf, PAGE_SIZE - 1, "%d\r\n", data->temp1_input); + break; + case TEMP1_MAX_HYST: + status = snprintf(buf, PAGE_SIZE - 1, "%d\r\n", TEMP1_MAX_HYST_DEFAULT); + break; + case TEMP1_MAX: + status = snprintf(buf, PAGE_SIZE - 1, "%d\r\n", TEMP1_MAX_DEFAULT); + break; + default : + break; + } + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t temp_info_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t size) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7716_32xb_thermal_data *data = i2c_get_clientdata(client); + long keyin = 0; + int status = -EINVAL; + + mutex_lock(&data->update_lock); + status = kstrtol(buf, STRING_TO_DEC_VALUE, &keyin); + switch (attr->index) + { + case TEMP1_INPUT: + data->temp1_input=keyin; + break; + case TEMP1_MAX_HYST: + data->temp1_max_hyst=keyin; + break; + case TEMP1_MAX: + data->temp1_max=keyin; + break; + default : + break; + } + mutex_unlock(&data->update_lock); + return size; + +} + +static const struct attribute_group as7716_32xb_thermal_group = { + .attrs = as7716_32xb_thermal_attributes, +}; + +static int as7716_32xb_thermal_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7716_32xb_thermal_data *data; + int status; + + data = kzalloc(sizeof(struct as7716_32xb_thermal_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + i2c_set_clientdata(client, data); + data->index = dev_id->driver_data; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7716_32xb_thermal_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: thermal '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_thermal_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7716_32xb_thermal_remove(struct i2c_client *client) +{ + struct as7716_32xb_thermal_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7716_32xb_thermal_group); + kfree(data); + + return 0; +} + + +static const struct i2c_device_id as7716_32xb_thermal_id[] = { + { "as7716_32xb_thermal", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7716_32xb_thermal_id); + +static struct i2c_driver as7716_32xb_thermal_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7716_32xb_thermal", + }, + .probe = as7716_32xb_thermal_probe, + .remove = as7716_32xb_thermal_remove, + .id_table = as7716_32xb_thermal_id, + .address_list = normal_i2c, +}; + + + + + + +static int __init as7716_32xb_thermal_init(void) +{ + return i2c_add_driver(&as7716_32xb_thermal_driver); +} + +static void __exit as7716_32xb_thermal_exit(void) +{ + i2c_del_driver(&as7716_32xb_thermal_driver); +} + +module_init(as7716_32xb_thermal_init); +module_exit(as7716_32xb_thermal_exit); + +MODULE_AUTHOR("Jostar yang "); +MODULE_DESCRIPTION("as7716_32xb_thermal driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_i2c_cpld.c b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_i2c_cpld.c new file mode 100755 index 000000000000..46d1e2773287 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/modules/accton_i2c_cpld.c @@ -0,0 +1,259 @@ +/* + * A hwmon driver for the accton_i2c_cpld + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CPLD_VERSION_REG 0x1 + +enum as5712_54x_cpld_sysfs_attributes { + CPLD_READ_VERSION, + CPLD_BYTE_ACCESS, + CPLD_DUMP_ALL, +}; + +static ssize_t read_cpld_version(struct device *dev, struct device_attribute *da, + char *buf); +int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); + + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +/* Addresses scanned for accton_i2c_cpld + */ +static const unsigned short normal_i2c[] = { 0x31, 0x35, 0x60, 0x61, 0x62, I2C_CLIENT_END }; + +static SENSOR_DEVICE_ATTR(cpld_get_version, S_IRUGO, read_cpld_version, NULL, CPLD_READ_VERSION); + +static struct attribute *as5712_54x_cpld_attributes[] = { + &sensor_dev_attr_cpld_get_version.dev_attr.attr, + NULL +}; + +static const struct attribute_group as5712_54x_cpld_group = { + .attrs = as5712_54x_cpld_attributes, +}; + +static ssize_t read_cpld_version(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + unsigned short cpld_reg = CPLD_VERSION_REG; + u8 reg; + + if(attr->index == CPLD_READ_VERSION) { + reg = accton_i2c_cpld_read(client->addr, cpld_reg); + return sprintf(buf, "%02x\n",reg); + } + return -1 ; +} + + +static void accton_i2c_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void accton_i2c_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + + mutex_unlock(&list_lock); +} + +static int accton_i2c_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_dbg(&client->dev, "i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as5712_54x_cpld_group); + if (status) { + goto exit; + } + + dev_info(&client->dev, "chip found\n"); + accton_i2c_cpld_add_client(client); + + return 0; +exit: + return status; +} + +static int accton_i2c_cpld_remove(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &as5712_54x_cpld_group); + + accton_i2c_cpld_remove_client(client); + return 0; +} + +static const struct i2c_device_id accton_i2c_cpld_id[] = { + { "accton_i2c_cpld", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, accton_i2c_cpld_id); + +static struct i2c_driver accton_i2c_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "accton_i2c_cpld", + }, + .probe = accton_i2c_cpld_probe, + .remove = accton_i2c_cpld_remove, + .id_table = accton_i2c_cpld_id, + .address_list = normal_i2c, +}; + +int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = i2c_smbus_read_byte_data(cpld_node->client, reg); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(accton_i2c_cpld_read); + +int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = i2c_smbus_write_byte_data(cpld_node->client, reg, value); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(accton_i2c_cpld_write); + +static int __init accton_i2c_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&accton_i2c_cpld_driver); +} + +static void __exit accton_i2c_cpld_exit(void) +{ + i2c_del_driver(&accton_i2c_cpld_driver); +} +/* +static struct dmi_system_id as7712_dmi_table[] = { + { + .ident = "Accton AS7712", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS7712"), + }, + } +}; + +int platform_accton_as7712_32x(void) +{ + return dmi_check_system(as7712_dmi_table); +} +EXPORT_SYMBOL(platform_accton_as7712_32x); +*/ +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_i2c_cpld driver"); +MODULE_LICENSE("GPL"); + +module_init(accton_i2c_cpld_init); +module_exit(accton_i2c_cpld_exit); diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/service/as7716_32xb-platform-monitor.service b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/service/as7716_32xb-platform-monitor.service new file mode 100755 index 000000000000..05a5d93c48b9 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/service/as7716_32xb-platform-monitor.service @@ -0,0 +1,17 @@ +[Unit] +Description=Accton AS7716-32XB Platform Monitoring service +Before=pmon.service +After=sysinit.target +DefaultDependencies=no + +[Service] +ExecStartPre=/usr/local/bin/accton_as7716_32xb_util.py install +ExecStart=/usr/local/bin/accton_as7716_32xb_drv_handler.py +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/setup.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/setup.py new file mode 100755 index 000000000000..3edfc29b3817 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/setup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +import os +import sys +from setuptools import setup +os.listdir + +setup( + name='as7716_32xb', + version='1.0', + description='Module to initialize Accton AS7716-32XB platforms', + + packages=['as7716_32xb'], + package_dir={'as7716_32xb': 'as7716-32xb/classes'}, +) + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/utils/README b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/utils/README new file mode 100755 index 000000000000..c602d753301e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/utils/README @@ -0,0 +1,117 @@ +Copyright (C) 2016 Accton Networks, Inc. + +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 3 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, see . + +Contents of this package: + patch - files under patch/ is for kernel and ONIE installer + for the kernel: + config-accton-as5712_54x.patch + for kernel configuration. + driver-i2c-muxes-pca954x-always-deselect.patch + for i2c_mux deselects after transaction. + driver-patches-for-accton-as5712-fan-psu-cpld.patch + for as5712's fan/psu/cpld/led/sfp drivers. + for ONIE: + onie_installer-accton-AS5712-54X.patch + for console port setting and copy util script o rootfs. + module - Contains source code of as5712 kernel driver modules. + +The late Sonic building scripts, pushed @Dec 5 2016, will automatically +create a docker container and run building process under it. +User is not necessary to handle docker environment creation. + +1. Download sonic-buildimage environment. + - Run "git clone https://github.com/Azure/sonic-buildimage". + - cd to sonic-buildimage and run "git submodule update --init --recursive". +2. Build kernel + - cd ./src/sonic-linux-kernel + - Copy patches and series from patch/kernel of this release to + sonic-linux-kernel/patch. + - Build kernel by "make". + - The built kernel package, linux-image-3.16.0-4-amd64_3.16.36-1+deb8u2_amd64.deb + , is generated. +3. Build installer + - Change directory back to sonic-buildimage/. + - Get onie_installer-accton-AS5712-54X.patch" from patch/installer. + - Change setting for AS5712-54X by patching build_image.sh. + "patch -p1 < onie_installer-accton-AS5712-54X.patch" + !!NOTICE, patching onie_installer-accton-AS5712-54X.patch comments out the + "git status" checking at build_image.sh. + - The account and password of installed OS can be given at rules/config. + The default user and password are "admin" & "YourPaSsWoRd" respectively. + - Run "make configure PLATFORM=broadcom" + - Copy the built kernel debian package to target/debs/. + The file is linux-image-3.16.0-4-amd64_*_amd64.deb under directory + src/sonic-linux-kernel/. + - Run "make target/sonic-generic.bin" + - Get the installer, target/sonic-generic.bin, to target machine and install. + +All Linux kernel code is licensed under the GPLv1. All other code is +licensed under the GPLv3. Please see the LICENSE file for copies of +both licenses. + +The code for integacting with Accton AS5712-54X has 2 parts, +kernel drivers and operational script. +The kernel drivers of peripherals are under module/ directory. +1. These drivers are patched into kernel by + driver-patches-for-accton-as5712-fan-psu-cpld.patch + Or you can build the driver under module/ by setting environment variable, + KERNEL_SRC, to proper linux built directory and run make. + It may be sonic-linux-kernel/linux-3.*/debian/build/build_amd64_none_amd64/. +2. A operational script, accton_as5712_util.py, for device initializatian and + peripheral accessing should be installed at /usr/bin. + This script is generated by onie_installer-accton-AS5712-54X.patch. + It's done by patching onie_installer-accton-AS5712-54X.patch at build-image. + Run "accton_as5712_util.py install" to install drivers. + +To initialize the system, run "accton_as5712_util.py install". +To clean up the drivers & devices, run "accton_as5712_util.py clean". +To dump information of sensors, run "accton_as5712_util.py show". +To dump SFP EEPROM, run "accton_as5712_util.py sff". +To set fan speed, run "accton_as5712_util.py set fan". +To enable/disable SFP emission, run "accton_as5712_util.py set sfp". +To set system LEDs' color, run "accton_as5712_util.py set led" +For more information, run "accton_as5712_util.py --help". + +==================================================================== +Besides applying accton_as5712_util.py to access peripherals, you can +access peripherals by sysfs nodes directly after the installation is run. + +System LED: + There are 5 system LEDs at the lower-left corner of front panel. + They are loc, diag, fan, ps1, and ps2. + The sysfs interface color mappings are as follows: + Brightness: + 0 => off + 1 => green + 2 => amber + 3 => red + 4 => blue + But not all colors are available for each LED. + +Fan Control: + There are 10 fans inside 5 fan modules. + All fans share 1 duty setting, ranged from 0~100. + +Thermal sensers: + 3 temperature sensors are controlled by the lm75 kernel modules. + +PSUs: + There 2 power supplies slot at the left/right side of the back. + Once if a PSU is not plugged, the status of it is shown failed. + +There are 48 SFP+ and 6 QSFP modules are equipped. +Before operating on PSU and QSFP+, please make sure it is well plugged. +Otherwise, operation is going to fail. + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/utils/accton_as7716_32xb_drv_handler.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/utils/accton_as7716_32xb_drv_handler.py new file mode 100755 index 000000000000..c08a82c7dc45 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/utils/accton_as7716_32xb_drv_handler.py @@ -0,0 +1,443 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 1/18/2018: Jostar create for as7716_32xb +# ------------------------------------------------------------------ + +try: + import os + import sys, getopt + import subprocess + import click + import imp + import logging + import logging.config + import types + import time # this is only being used as part of the example + import traceback + import commands + from tabulate import tabulate +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + +# Deafults +VERSION = '1.0' +FUNCTION_NAME = 'as7716_32xb_drv_handler' +DEBUG = False + +global log_file +global log_level + +def my_log(txt): + if DEBUG == True: + print "[ACCTON DBG]: "+txt + return + +def log_os_system(cmd, show): + logging.info('Run :'+cmd) + status = 1 + output = "" + status, output = commands.getstatusoutput(cmd) + if DEBUG == True: + my_log (cmd +" , result:" + str(status)) + else: + if show: + print "ACC: " + str(cmd) + " , result:"+ str(status) + #my_log ("cmd:" + cmd) + #my_log (" output:"+output) + if status: + logging.info('Failed :'+cmd) + if show: + print('Failed :'+cmd) + return status, output + + +# Make a class we can use to capture stdout and sterr in the log +class accton_as7716xb_drv_handler(object): + + QSFP_PORT_START = 1 + QSFP_PORT_END = 32 + BASE_PATH = "/usr/local/bin/" + BASE_I2C_PATH="/sys/bus/i2c/devices/" + QSFP_PRESENT_PATH = "/sys/bus/i2c/devices/0-0060/module_present_" + QSFP_RESET_PATH = "/sys/bus/i2c/devices/0-0060/module_reset_" + QSFP_PRESENT_FILE = "/tmp/ipmi_qsfp_pres" + QSFP_EEPROM_FILE = "/tmp/ipmi_qsfp_ee_" + THERMAL_FILE = "/tmp/ipmi_thermal" + IPMI_CMD_QSFP = "ipmitool raw 0x34 0x10 " + IPMI_CMD_THERMAL = "ipmitool raw 0x34 0x12 " + IPMI_CMD_FAN = "ipmitool raw 0x34 0x14 " + IPMI_CMD_PSU ="ipmitool raw 0x34 0x16 " + IPMI_CMD_SYS_EEPROM_1 ="ipmitool raw 0x34 0x18 0x0 0x80" + IPMI_CMD_SYS_EEPROM_2 ="ipmitool raw 0x34 0x18 0x80 0x80" + FAN_ID_START = 1 + FAN_ID_END = 6 + FAN_FILE = "/tmp/ipmi_fan" + FAN_PATH = "/sys/bus/i2c/devices/0-0066/fan" + PSU_ID_START = 1 + PSU_ID_END = 2 + PSU1_PATH = "/sys/bus/i2c/devices/0-0053/" + PSU2_PATH = "/sys/bus/i2c/devices/0-0050/" + PSU1_PMBUS_PATH = "/sys/bus/i2c/devices/0-005b/" + PSU2_PMBUS_PATH = "/sys/bus/i2c/devices/0-0058/" + PSU_FILE = "/tmp/ipmi_psu_" + SYS_EEPROM_FILE_1 = "/tmp/ipmi_sys_eeprom_1" + SYS_EEPROM_FILE_2 = "/tmp/ipmi_sys_eeprom_2" + SYS_EEPROM_PATH = "/sys/bus/i2c/devices/0-0056/eeprom" + + + def __init__(self, log_file, log_level): + """Needs a logger and a logger level.""" + # set up logging to file + logging.basicConfig( + filename=log_file, + filemode='w', + level=log_level, + format= '[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s', + datefmt='%H:%M:%S' + ) + + # set up logging to console + if log_level == logging.DEBUG: + console = logging.StreamHandler() + console.setLevel(log_level) + formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + + logging.debug('SET. logfile:%s / loglevel:%d', log_file, log_level) + + def manage_ipmi_qsfp(self): + logging.debug ("drv hanlder-manage_ipmi_qsfp") + print "drv hanlder" + #Handle QSFP case + ipmi_cmd = self.IPMI_CMD_QSFP + " 0x10 > " +self.QSFP_PRESENT_FILE + log_os_system(ipmi_cmd, 0) + file_path = self.QSFP_PRESENT_FILE + check_file = open(file_path) + try: + check_file = open(file_path) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + line = check_file.readline() + pres_line= line.rstrip().replace(" ","") + while line: + line = check_file.readline() + pres_line+=line.rstrip().replace(" ","") + check_file.close() + + + for i in range(self.QSFP_PORT_START, self.QSFP_PORT_END+1, 1): + if i>1: + k=(i-1)*2 +1 + else: + k=1; + if pres_line[k] == '1': + #Set QSFP present + #print "port-%d present" %i + set_drv_cmd = "echo 1 > " + self.QSFP_PRESENT_PATH + str(i) + log_os_system(set_drv_cmd, 0) + #Read QSFP EEPROM + ipmi_cmd = self.IPMI_CMD_QSFP +str(i)+ " 0x00 > " +self.QSFP_EEPROM_FILE + str(i) + "_1" + log_os_system(ipmi_cmd, 0) + ipmi_cmd = self.IPMI_CMD_QSFP +str(i)+ " 0x01 > " +self.QSFP_EEPROM_FILE + str(i) + "_2" + log_os_system(ipmi_cmd, 0) + file_path = self.QSFP_EEPROM_FILE + str(i) + "_1" + check_file = open(file_path) + try: + check_file = open(file_path) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + line = check_file.readline() + str_line= line.rstrip().replace(" ","") + while line: + line = check_file.readline() + str_line+=line.rstrip().replace(" ","") + check_file.close() + file_path = self.QSFP_EEPROM_FILE + str(i) + "_2" + check_file = open(file_path) + try: + check_file = open(file_path) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + line = check_file.readline() + str_line+= line.rstrip().replace(" ","") + + while line: + line = check_file.readline() + str_line+=line.rstrip().replace(" ","") + check_file.close() + #Set QSFP EEPROM + if(i < 10): + set_drv_cmd = "echo " + str_line+ " > " + self.BASE_I2C_PATH + "0-000"+str(i) +"/eeprom" + else: + set_drv_cmd = "echo " + str_line+ " > " + self.BASE_I2C_PATH + "0-00"+str(i) +"/eeprom" + #print(set_drv_cmd) + log_os_system(set_drv_cmd, 0) + else: + ipmi_cmd = "echo 0 > " + self.QSFP_PRESENT_PATH + str(i) + log_os_system(ipmi_cmd, 0) + if(i < 10): + set_drv_cmd = "echo 0 > " + self.BASE_I2C_PATH + "0-000"+str(i) +"/eeprom" + else: + set_drv_cmd = "echo 0 > " + self.BASE_I2C_PATH + "0-00"+str(i) +"/eeprom" + #print(set_drv_cmd) + log_os_system(set_drv_cmd, 0) + + time.sleep(0.01) + return True + + def manage_ipmi_thermal(self): + logging.debug ("drv hanlder-manage_ipmi_thermal") + #Handle thermal case + #ipmitool raw 0x34 0x12 + ipmi_cmd = self.IPMI_CMD_THERMAL + " > " +self.THERMAL_FILE + log_os_system(ipmi_cmd, 0) + file_path = self.THERMAL_FILE + check_file = open(file_path) + try: + check_file = open(file_path) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + line = check_file.readline() + str_line= line.rstrip().replace(" ","") + while line: + line = check_file.readline() + str_line+=line.rstrip().replace(" ","") + check_file.close() + val_str= "0x" + str(str_line[4])+str(str_line[5]) + val_int=int(val_str, 16)*1000 + check_file.close() + set_drv_cmd = "echo "+str(val_int) + " > " + self.BASE_I2C_PATH + "0-0048/temp1_input" + log_os_system(set_drv_cmd,0) + val_str= "0x" + str(str_line[10])+str(str_line[11]) + val_int=int(val_str, 16) * 1000 + set_drv_cmd = "echo "+str(val_int) + " > " + self.BASE_I2C_PATH + "0-0049/temp1_input" + log_os_system(set_drv_cmd,0) + val_str= "0x" + str(str_line[16])+str(str_line[17]) + val_int=int(val_str, 16) * 1000 + set_drv_cmd = "echo "+str(val_int) + " > " + self.BASE_I2C_PATH + "0-004a/temp1_input" + log_os_system(set_drv_cmd, 0) + + return True + + def manage_ipmi_fan(self): + logging.debug ("drv hanlder-manage_ipmi_fan") + #Handle fan case + #ipmitool raw 0x34 0x14 + ipmi_cmd = self.IPMI_CMD_FAN + " > " +self.FAN_FILE + log_os_system(ipmi_cmd, 0) + file_path = self.FAN_FILE + #print(file_path) + check_file = open(file_path) + try: + check_file = open(file_path) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + line = check_file.readline() + str_line= line.rstrip().replace(" ","") + while line: + line = check_file.readline() + str_line+=line.rstrip().replace(" ","") + check_file.close() + #print (str_line) + k=0 + for i in range(self.FAN_ID_START, self.FAN_ID_END+1, 1): + #print "k=%d"%k + if str_line[k+1]=='0': + set_drv_cmd = "echo 1 > " + self.FAN_PATH + str(i) + "_present" + else: + set_drv_cmd = "echo 0 > " + self.FAN_PATH + str(i) + "_present" + log_os_system(set_drv_cmd, 0) + + val_str= "0x" + str(str_line[k+6])+str(str_line[k+7]) + str(str_line[k+4])+str(str_line[k+5]) + val_int=int(val_str, 16) + set_drv_cmd = "echo " + str(val_int) + " > " + self.FAN_PATH + str(i) + "_front_speed_rpm" + log_os_system(set_drv_cmd, 0) + val_str= "0x" + str(str_line[k+54])+str(str_line[k+55]) + str(str_line[k+52])+str(str_line[k+53]) + val_int=int(val_str, 16) + set_drv_cmd = "echo " + str(val_int) + " > " + self.FAN_PATH + str(i) + "_rear_speed_rpm" + log_os_system(set_drv_cmd, 0) + k+=8; + return True + + + def manage_ipmi_psu(self): + logging.debug ("drv hanlder-manage_ipmi_psu") + #Handle psu case + #present: ipmitool raw 0x34 0x16 '0x1' . Param-1 is psu id(id_1:0x1, id_2:0x2) + + #cpld access psu + for i in range(self.PSU_ID_START, self.PSU_ID_END+1, 1): + #present case + ipmi_cmd = self.IPMI_CMD_PSU + str(i) + " > " +self.PSU_FILE + str(i) + log_os_system(ipmi_cmd, 0) + if i==1: + psu_sysfs_path = self.PSU1_PATH + else: + psu_sysfs_path = self.PSU2_PATH + file_path = self.PSU_FILE + str(i) + #print(file_path) + check_file = open(file_path) + try: + check_file = open(file_path) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + line = check_file.readline() + str_line= line.rstrip().replace(" ","") + while line: + line = check_file.readline() + str_line+=line.rstrip().replace(" ","") + check_file.close() + #print (line) + if str_line[1]=='0': + int_val=1 #psu insert + #print "psu_%d present"%i + set_drv_cmd = "echo " +str(int_val) + " > " + psu_sysfs_path + "psu_present" + log_os_system(set_drv_cmd, 0) + set_drv_cmd = "echo " + str(str_line[5]) + " > " + psu_sysfs_path + "psu_power_good" + log_os_system(set_drv_cmd, 0) + else: + int_val=0 + set_drv_cmd = "echo " +str(int_val) + " > " + psu_sysfs_path + "psu_present" + log_os_system(set_drv_cmd, 0) + set_drv_cmd = "echo " + str(int_val) + " > " + psu_sysfs_path + "psu_power_good" + log_os_system(set_drv_cmd, 0) + + #pmbus + if i==1: + psu_sysfs_path = self.PSU1_PMBUS_PATH + else: + psu_sysfs_path = self.PSU2_PMBUS_PATH + + if str_line[5]=='1': #power_on + val_str= "0x" + str(str_line[28])+str(str_line[29]) + str(str_line[26])+str(str_line[27]) + val_int=int(val_str, 16) * 1000 + set_drv_cmd = "echo " + str(val_int) + " > " + psu_sysfs_path + "psu_temp1_input" + log_os_system(set_drv_cmd, 0) + #print "val_int=%d"%val_int + val_str= "0x" + str(str_line[32])+str(str_line[33]) + str(str_line[30])+str(str_line[31]) + val_int=int(val_str, 16) + #print "fan:val_int=%d"%val_int + set_drv_cmd = "echo " + str(val_int) + " > " + psu_sysfs_path + "psu_fan1_speed_rpm" + log_os_system(set_drv_cmd, 0) + val_str= "0x" + str(str_line[36])+str(str_line[37]) + str(str_line[34])+str(str_line[35]) + val_int=int(val_str, 16) + #print "pout val_int=%d"%val_int + set_drv_cmd = "echo " + str(val_int) + " > " + psu_sysfs_path + "psu_p_out" + log_os_system(set_drv_cmd, 0) + else: #power_off + val_int=0 + set_drv_cmd = "echo " + str(val_int) + " > " + psu_sysfs_path + "psu_temp1_input" + log_os_system(set_drv_cmd, 0) + set_drv_cmd = "echo " + str(val_int) + " > " + psu_sysfs_path + "psu_fan1_speed_rpm" + log_os_system(set_drv_cmd, 0) + set_drv_cmd = "echo " + str(val_int) + " > " + psu_sysfs_path + "psu_p_out" + log_os_system(set_drv_cmd, 0) + + time.sleep(2) + return True + + def manage_ipmi_sys(self): + logging.debug ("drv hanlder-manage_ipmi_sys") + #Handle sys case + #ipmitool -raw 0x34 0x18 0x00 0x80 + #ipmitool -raw 0x34 0x18 0x80 0x80 + + ipmi_cmd = self.IPMI_CMD_SYS_EEPROM_1 + " > " + self.SYS_EEPROM_FILE_1 + log_os_system(ipmi_cmd, 0) + ipmi_cmd = self.IPMI_CMD_SYS_EEPROM_2 + " > " + self.SYS_EEPROM_FILE_2 + log_os_system(ipmi_cmd, 0) + + #Read SYS EEPROM + file_path = self.SYS_EEPROM_FILE_1 + check_file = open(file_path) + try: + check_file = open(file_path) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + line = check_file.readline() + str_line= line.rstrip().replace(" ","") + while line: + line = check_file.readline() + str_line+=line.rstrip().replace(" ","") + check_file.close() + + file_path = self.SYS_EEPROM_FILE_2 + check_file = open(file_path) + try: + check_file = open(file_path) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + line = check_file.readline() + str_line+= line.rstrip().replace(" ","") + while line: + line = check_file.readline() + str_line+=line.rstrip().replace(" ","") + check_file.close() + #print(len(str_line)) + #print(str_line) + set_drv_cmd = "echo " + str_line+ " > " + self.SYS_EEPROM_PATH + #print(set_drv_cmd) + log_os_system(set_drv_cmd, 0) + + return True + +def main(argv): + log_file = '%s.log' % FUNCTION_NAME + log_level = logging.WARNING + if len(sys.argv) != 1: + try: + opts, args = getopt.getopt(argv,'hdl:',['lfile=']) + except getopt.GetoptError: + print 'Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + for opt, arg in opts: + if opt == '-h': + print 'Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + elif opt in ('-d', '--debug'): + log_level = logging.DEBUG + elif opt in ('-l', '--lfile'): + log_file = arg + + set_drv_cmd = "echo 100 > /sys/module/ipmi_si/parameters/kipmid_max_busy_us" + log_os_system(set_drv_cmd, 0) + monitor = accton_as7716xb_drv_handler(log_file, log_level) + + set_sys_eeprom=0 + thermal_chk_time=0 + psu_chk_time=0 + # Loop forever, doing something useful hopefully: + while True: + logging.debug ("monitor.manage_ipmi") + if set_sys_eeprom==0: + monitor.manage_ipmi_sys() + set_sys_eeprom=1 + monitor.manage_ipmi_qsfp() + time.sleep(0.1) + monitor.manage_ipmi_thermal() + monitor.manage_ipmi_psu() + time.sleep(0.1) + monitor.manage_ipmi_fan() + time.sleep(0.05) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/utils/accton_as7716_32xb_monitor.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/utils/accton_as7716_32xb_monitor.py new file mode 100755 index 000000000000..2568312b0955 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/utils/accton_as7716_32xb_monitor.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 11/13/2017: Polly Hsu, Create +# 1/10/2018: Jostar modify for as7716_32 +# ------------------------------------------------------------------ + +try: + import os + import sys, getopt + import subprocess + import click + import imp + import logging + import logging.config + import types + import time # this is only being used as part of the example + import traceback + from tabulate import tabulate + from as7716_32x.fanutil import FanUtil + from as7716_32x.thermalutil import ThermalUtil +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + +# Deafults +VERSION = '1.0' +FUNCTION_NAME = 'accton_as7716_monitor' + +global log_file +global log_level + + # For AC power Front to Back : + # If any fan fail, please fan speed register to 15 + # The max value of Fan speed register is 9 + # [LM75(48) + LM75(49) + LM75(4A)] > 174 => set Fan speed value from 4 to 5 + # [LM75(48) + LM75(49) + LM75(4A)] > 182 => set Fan speed value from 5 to 7 + # [LM75(48) + LM75(49) + LM75(4A)] > 190 => set Fan speed value from 7 to 9 + # + # [LM75(48) + LM75(49) + LM75(4A)] < 170 => set Fan speed value from 5 to 4 + # [LM75(48) + LM75(49) + LM75(4A)] < 178 => set Fan speed value from 7 to 5 + # [LM75(48) + LM75(49) + LM75(4A)] < 186 => set Fan speed value from 9 to 7 + # + # + # For AC power Back to Front : + # If any fan fail, please fan speed register to 15 + # The max value of Fan speed register is 10 + # [LM75(48) + LM75(49) + LM75(4A)] > 140 => set Fan speed value from 4 to 5 + # [LM75(48) + LM75(49) + LM75(4A)] > 150 => set Fan speed value from 5 to 7 + # [LM75(48) + LM75(49) + LM75(4A)] > 160 => set Fan speed value from 7 to 10 + # + # [LM75(48) + LM75(49) + LM75(4A)] < 135 => set Fan speed value from 5 to 4 + # [LM75(48) + LM75(49) + LM75(4A)] < 145 => set Fan speed value from 7 to 5 + # [LM75(48) + LM75(49) + LM75(4A)] < 155 => set Fan speed value from 10 to 7 + # + + + # 2.If no matched fan speed is found from the policy, + # use FAN_DUTY_CYCLE_MIN as default speed + # Get current temperature + # 4.Decision 3: Decide new fan speed depend on fan direction/current fan speed/temperature + + + + +# Make a class we can use to capture stdout and sterr in the log +class accton_as7716_monitor(object): + # static temp var + _ori_temp = 0 + _new_perc = 0 + _ori_perc = 0 + + def __init__(self, log_file, log_level): + """Needs a logger and a logger level.""" + # set up logging to file + logging.basicConfig( + filename=log_file, + filemode='w', + level=log_level, + format= '[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s', + datefmt='%H:%M:%S' + ) + + # set up logging to console + if log_level == logging.DEBUG: + console = logging.StreamHandler() + console.setLevel(log_level) + formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + + logging.debug('SET. logfile:%s / loglevel:%d', log_file, log_level) + + def manage_fans(self): + + fan_policy_f2b = { + 0: [32, 0, 174000], + 1: [38, 170000, 182000], + 2: [50, 178000, 190000], + 3: [63, 186000, 0], + } + fan_policy_b2f = { + 0: [32, 0, 140000], + 1: [38, 135000, 150000], + 2: [50, 145000, 160000], + 3: [69, 15500, 0], + } + + thermal = ThermalUtil() + fan = FanUtil() + get_temp = thermal.get_thermal_temp() + # 1. Get each fan status, one not presented, set speed to full + # Get fan direction (Only get the first one since all fan direction are the same) + # Get current fan duty cycle + cur_duty_cycle = fan.get_fan_duty_cycle() + #print "cur_duty_cycle=%d" %cur_duty_cycle + #print "get_temp=%d" %get_temp + + for x in range(fan.get_idx_fan_start(), fan.get_num_fans()+1): + fan_status = fan.get_fan_status(x) + if fan_status is None: + #print "fan_status is None" + logging.debug('INFO. SET new_perc to %d (FAN stauts is None. fan_num:%d)', 100, x) + return False + if fan_status is False: + #self._new_perc = FAN_LEV1_SPEED_PERC + #print "fan_%d status=false, set 45 duty_cycle" %x + logging.debug('INFO. SET new_perc to %d (FAN fault. fan_num:%d)', 100, x) + fan.set_fan_duty_cycle(45) + return True + logging.debug('INFO. fan_status is True (fan_num:%d)', x) + + if fan_status is not None and fan_status is not False: + fan_dir=fan.get_fan_dir(1) + for x in range(0, 4): + if cur_duty_cycle == fan_policy_f2b[x][0]: + break + #print "x=%d" %x + #print "fan_dir=%d" %fan_dir + #print "fan_policy_f2b[x][0]=%d" %fan_policy_f2b[x][0] + #print "cur_duty_cycle=%d" %cur_duty_cycle + + if fan_dir == 1: + if x == 4 : + fan.set_fan_duty_cycle(fan_policy_f2b[0][0]) + new_duty_cycle=cur_duty_cycle + # if temp > up_levle, else if temp < down_level + if get_temp > fan_policy_f2b[x][2] and x != 3 : + new_duty_cycle= fan_policy_f2b[x+1][0] + #print "new_duty_cycle= fan_policy_f2b[x+1][0]=%d" %new_duty_cycle + #print "Becasue get_temp > fan_policy_f2b[x][2]=%d" %fan_policy_f2b[x][2] + logging.debug('INFO. THERMAL temp UP, temp %d > %d , new_duty_cycle=%d', get_temp, fan_policy_f2b[x][2], new_duty_cycle) + #else get_temp < fan_policy_f2b[x][1] and x != 0 : + elif get_temp < fan_policy_f2b[x][1] : + new_duty_cycle= fan_policy_f2b[x-1][0] + #print "new_duty_cycle= fan_policy_f2b[x-1][0]=%d" %new_duty_cycle + #print "Becasue get_temp < fan_policy_f2b[x][1]=%d" %fan_policy_f2b[x][1] + logging.debug('INFO. THERMAL temp down, temp %d < %d , new_duty_cycle=%d', get_temp, fan_policy_f2b[x][1], new_duty_cycle) + if new_duty_cycle == cur_duty_cycle : + #print "new_duty_cycle == cur_duty_cycle case, so return True" + return True + else: + if x == 4 : + fan.set_fan_duty_cycle(fan_policy_b2f[0][0]) + new_duty_cycle=cur_duty_cycle + # if temp > up_levle, else if temp < down_level + if get_temp > fan_policy_b2f[x][1] and x != 3 : + new_duty_cycle= fan_policy_b2f[x+1][0] + logging.debug('INFO. THERMAL temp UP, temp %d > %d , new_duty_cycle=%d', get_temp, fan_policy_b2f[x][2], new_duty_cycle) + elif get_temp < fan_policy_b2f[x][0] and x != 0 : + #elif get_temp < fan_policy_b2f[x][0] + new_duty_cycle= fan_policy_b2f[x-1][0] + logging.debug('INFO. THERMAL temp down, temp %d < %d , new_duty_cycle=%d', get_temp, fan_policy_b2f[x][1], new_duty_cycle) + if new_duty_cycle == cur_duty_cycle : + return True + + fan.set_fan_duty_cycle(new_duty_cycle) + #print "set new_duty_cycle=%d" %new_duty_cycle + #print "old_duty_cycle=%d" %cur_duty_cycle + + return True + + + +def main(argv): + log_file = '%s.log' % FUNCTION_NAME + log_level = logging.INFO + if len(sys.argv) != 1: + try: + opts, args = getopt.getopt(argv,'hdl:',['lfile=']) + except getopt.GetoptError: + print 'Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + for opt, arg in opts: + if opt == '-h': + print 'Usage: %s [-d] [-l ]' % sys.argv[0] + return 0 + elif opt in ('-d', '--debug'): + log_level = logging.DEBUG + elif opt in ('-l', '--lfile'): + log_file = arg + + monitor = accton_as7716_monitor(log_file, log_level) + + # Loop forever, doing something useful hopefully: + while True: + monitor.manage_fans() + time.sleep(1) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/utils/accton_as7716_32xb_util.py b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/utils/accton_as7716_32xb_util.py new file mode 100755 index 000000000000..ffddfb338506 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7716-32xb/utils/accton_as7716_32xb_util.py @@ -0,0 +1,616 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 Accton Networks, Inc. +# +# 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 3 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, see . + +""" +Usage: %(scriptName)s [options] command object + +options: + -h | --help : this help message + -d | --debug : run with debug mode + -f | --force : ignore error during installation or clean +command: + install : install drivers and generate related sysfs nodes + clean : uninstall drivers and remove related sysfs nodes + show : show all systen status + sff : dump SFP eeprom + set : change board setting with fan|led|sfp +""" + +import os +import commands +import sys, getopt +import logging +import re +import time +from collections import namedtuple + +PROJECT_NAME = 'as7716_32xb' +version = '0.0.1' +verbose = False +DEBUG = False +args = [] +ALL_DEVICE = {} +DEVICE_NO = {'led':5, 'fan1':1, 'fan2':1,'fan3':1,'fan4':1,'fan5':1,'thermal':3, 'psu':2, 'sfp':54} + + +led_prefix ='/sys/devices/platform/as7716_32xb_led/leds/accton_'+PROJECT_NAME+'_led::' +fan_prefix ='/sys/devices/platform/as7716_32xb_' +hwmon_types = {'led': ['diag','fan','loc','psu1','psu2'], + 'fan1': ['fan'], + 'fan2': ['fan'], + 'fan3': ['fan'], + 'fan4': ['fan'], + 'fan5': ['fan'], + 'fan5': ['fan'], + } +hwmon_nodes = {'led': ['brightness'] , + 'fan1': ['fan_duty_cycle_percentage', 'fan1_fault', 'fan1_speed_rpm', 'fan1_direction', 'fanr1_fault', 'fanr1_speed_rpm'], + 'fan2': ['fan_duty_cycle_percentage','fan2_fault', 'fan2_speed_rpm', 'fan2_direction', 'fanr2_fault', 'fanr2_speed_rpm'], + 'fan3': ['fan_duty_cycle_percentage','fan3_fault', 'fan3_speed_rpm', 'fan3_direction', 'fanr3_fault', 'fanr3_speed_rpm'], + 'fan4': ['fan4_duty_cycle_percentage','fan4_fault', 'fan4_speed_rpm', 'fan4_direction', 'fanr4_fault', 'fanr4_speed_rpm'], + 'fan5': ['fan_duty_cycle_percentage','fan5_fault', 'fan5_speed_rpm', 'fan5_direction', 'fanr5_fault', 'fanr5_speed_rpm'], + } +hwmon_prefix ={'led': led_prefix, + 'fan1': fan_prefix, + 'fan2': fan_prefix, + 'fan3': fan_prefix, + 'fan4': fan_prefix, + 'fan5': fan_prefix, + } + +i2c_prefix = '/sys/bus/i2c/devices/' +i2c_drv_prefix = '/sys/bus/i2c/devices/' +i2c_bus = {'thermal': ['10-0048','10-0049', '10-004a'] , + 'psu': ['17-0050','18-0053'], + 'sfp': ['-0050']} +i2c_nodes = { + 'thermal': ['hwmon/hwmon*/temp1_input'] , + 'psu': ['psu_present ', 'psu_power_good'] , + 'sfp': ['sfp_is_present ', 'sfp_tx_disable']} + +sfp_map = [1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32] + +mknod =[ +'echo as7716_32xb_cpld1 0x60 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo accton_i2c_cpld 0x62 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo accton_i2c_cpld 0x64 > /sys/bus/i2c/devices/i2c-0/new_device', + +] + +mknod_xb =[ +'echo as7716_32xb_cpld1 0x60 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo accton_i2c_cpld 0x62 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo accton_i2c_cpld 0x64 > /sys/bus/i2c/devices/i2c-0/new_device', +#Thermal +'echo as7716_32xb_thermal 0x48 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo as7716_32xb_thermal 0x49 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo as7716_32xb_thermal 0x4a > /sys/bus/i2c/devices/i2c-0/new_device', +#Fan +'echo as7716_32xb_fan 0x66 > /sys/bus/i2c/devices/i2c-0/new_device', +# PSU-1 +'echo as7716_32xb_psu1 0x53 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo as7716_32xb_psu2 0x50> /sys/bus/i2c/devices/i2c-0/new_device', +# PSU-2 +'echo as7716_32xb_pmbus 0x5b > /sys/bus/i2c/devices/i2c-0/new_device', +'echo as7716_32xb_pmbus 0x58 > /sys/bus/i2c/devices/i2c-0/new_device', +#EERPOM +'echo as7716_32xb_sys 0x56 > /sys/bus/i2c/devices/i2c-0/new_device', +] + + + +mknod2 =[ +'echo as7716_32xb_cpld1 0x60 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo accton_i2c_cpld 0x61 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo accton_i2c_cpld 0x62 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-0/new_device', + +# PSU-1 +'echo as7716_32xb_psu1 0x38 > /sys/bus/i2c/devices/i2c-57/new_device', +'echo cpr_4011_4mxx 0x3c > /sys/bus/i2c/devices/i2c-57/new_device', +'echo as7716_32xb_psu1 0x50 > /sys/bus/i2c/devices/i2c-57/new_device', +'echo ym2401 0x58 > /sys/bus/i2c/devices/i2c-57/new_device', + +# PSU-2 +'echo as7716_32xb_psu2 0x3b > /sys/bus/i2c/devices/i2c-58/new_device', +'echo cpr_4011_4mxx 0x3f > /sys/bus/i2c/devices/i2c-58/new_device', +'echo as7716_32xb_psu2 0x53 > /sys/bus/i2c/devices/i2c-58/new_device', +'echo ym2401 0x5b > /sys/bus/i2c/devices/i2c-58/new_device', + +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-61/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-62/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-63/new_device', + +#EERPOM +#'echo 24c02 0x57 > /sys/bus/i2c/devices/i2c-1/new_device', +] + +FORCE = 0 +logging.basicConfig(filename= PROJECT_NAME+'.log', filemode='w',level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) + + +if DEBUG == True: + print sys.argv[0] + print 'ARGV :', sys.argv[1:] + + +def main(): + global DEBUG + global args + global FORCE + + if len(sys.argv)<2: + show_help() + + options, args = getopt.getopt(sys.argv[1:], 'hdf', ['help', + 'debug', + 'force', + ]) + if DEBUG == True: + print options + print args + print len(sys.argv) + + for opt, arg in options: + if opt in ('-h', '--help'): + show_help() + elif opt in ('-d', '--debug'): + DEBUG = True + logging.basicConfig(level=logging.INFO) + elif opt in ('-f', '--force'): + FORCE = 1 + else: + logging.info('no option') + for arg in args: + if arg == 'install': + do_install() + elif arg == 'clean': + do_uninstall() + elif arg == 'show': + device_traversal() + elif arg == 'sff': + if len(args)!=2: + show_eeprom_help() + elif int(args[1]) ==0 or int(args[1]) > DEVICE_NO['sfp']: + show_eeprom_help() + else: + show_eeprom(args[1]) + return + elif arg == 'set': + if len(args)<3: + show_set_help() + else: + set_device(args[1:]) + return + else: + show_help() + + + return 0 + +def show_help(): + print __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]} + sys.exit(0) + +def show_set_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print cmd +" [led|sfp|fan]" + print " use \""+ cmd + " led 0-4 \" to set led color" + print " use \""+ cmd + " fan 0-100\" to set fan duty percetage" + print " use \""+ cmd + " sfp 1-32 {0|1}\" to set sfp# tx_disable" + sys.exit(0) + +def show_eeprom_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print " use \""+ cmd + " 1-32 \" to dump sfp# eeprom" + sys.exit(0) + +def my_log(txt): + if DEBUG == True: + print "[ACCTON DBG]: "+txt + return + +def log_os_system(cmd, show): + logging.info('Run :'+cmd) + status = 1 + output = "" + status, output = commands.getstatusoutput(cmd) + my_log (cmd +"with result:" + str(status)) + my_log ("cmd:" + cmd) + my_log (" output:"+output) + if status: + logging.info('Failed :'+cmd) + if show: + print('Failed :'+cmd) + return status, output + +def driver_inserted(): + ret, lsmod = log_os_system("lsmod| grep accton", 0) + logging.info('mods:'+lsmod) + if len(lsmod) ==0: + return False + + +kos = [ +'depmod -ae', +'modprobe ipmi_msghandler', +'modprobe ipmi_si', +'modprobe ipmi_devintf', +'modprobe i2c_dev', +'modprobe accton_i2c_cpld', +'modprobe accton_as7716_32xb_cpld1', +'modprobe accton_as7716_32xb_fan', +'modprobe accton_as7716_32xb_leds', +'modprobe accton_as7716_32xb_psu', +'modprobe accton_as7716_32xb_oom', +'modprobe accton_as7716_32xb_thermal', +'modprobe accton_as7716_32xb_pmbus', +'modprobe accton_as7716_32xb_sys'] + +def driver_install(): + global FORCE + for i in range(0,len(kos)): + status, output = log_os_system(kos[i], 1) + if status: + if FORCE == 0: + return status + return 0 + +def driver_uninstall(): + global FORCE + for i in range(0,len(kos)): + rm = kos[-(i+1)].replace("modprobe", "modprobe -rq") + rm = rm.replace("insmod", "rmmod") + status, output = log_os_system(rm, 1) + if status: + if FORCE == 0: + return status + return 0 + + + +def i2c_order_check(): + # i2c bus 0 and 1 might be installed in different order. + # Here check if 0x76 is exist @ i2c-0 + tmp = "echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-1/new_device" + status, output = log_os_system(tmp, 0) + if not device_exist(): + order = 1 + else: + order = 0 + tmp = "echo 0x70 > /sys/bus/i2c/devices/i2c-1/delete_device" + status, output = log_os_system(tmp, 0) + return order + +def device_install(): + global FORCE + + #order = i2c_order_check() + # if 0x76 is not exist @i2c-0, use reversed bus order + #if order: + # for i in range(0,len(mknod2)): + # #for pca932x need times to built new i2c buses + # if mknod2[i].find('pca954') != -1: + # time.sleep(2) + + # status, output = log_os_system(mknod2[i], 1) + # if status: + # print output + # if FORCE == 0: + # return status + #else: + print "Prepar to create instance.............." + for i in range(0,len(mknod_xb)): + print "Beginn to create instance.............." + status, output = log_os_system(mknod_xb[i], 1) + print "status=%s" %status + print "output=%s" %output + if status: + print output + if FORCE == 0: + return status + #time.sleep (50.0 / 1000.0) + print "Create sfp instance.............." + for i in range(0,len(sfp_map)): + status, output =log_os_system("echo as7716_32xb_oom 0x"+str(sfp_map[i])+ " > /sys/bus/i2c/devices/i2c-0/new_device", 1) + if status: + print output + if FORCE == 0: + return status + #status, output =log_os_system("echo port"+str(i)+" > /sys/bus/i2c/devices/0-000"+str(sfp_map[i])+"/port_name", 1) + #if status: + # print output + # if FORCE == 0: + # return status + return + +def device_uninstall(): + global FORCE + + for i in range(0,len(sfp_map)): + target = "echo 0x"+str(sfp_map[i])+ " > /sys/bus/i2c/devices/i2c-0/delete_device" + print(target) + status, output =log_os_system(target, 1) + if status: + print output + if FORCE == 0: + return status + + nodelist = mknod_xb + + for i in range(len(nodelist)): + target = nodelist[-(i+1)] + temp = target.split() + del temp[1] + temp[-1] = temp[-1].replace('new_device', 'delete_device') + status, output = log_os_system(" ".join(temp), 1) + if status: + print output + if FORCE == 0: + return status + + return + +def system_ready(): + if driver_inserted() == False: + print "driver_inserted() == False" + return False + if not device_exist(): + print "not device_exist()" + return False + return True + +def do_install(): + print "Checking system...." + if driver_inserted() == False: + print "No driver, installing.1..." + status = driver_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" drivers detected...." + if not device_exist(): + print "No device, installing..2.." + status = device_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" devices detected...." + return + +def do_uninstall(): + print "Checking systemm...." + if not device_exist(): + print PROJECT_NAME.upper() +" has no device installed...." + else: + print "Removing device...." + status = device_uninstall() + if status: + if FORCE == 0: + return status + + if driver_inserted()== False : + print PROJECT_NAME.upper() +" has no driver installed...." + else: + print "Removing installed driver...." + status = driver_uninstall() + if status: + if FORCE == 0: + return status + + return + +def devices_info(): + global DEVICE_NO + global ALL_DEVICE + global i2c_bus, hwmon_types + for key in DEVICE_NO: + ALL_DEVICE[key]= {} + for i in range(0,DEVICE_NO[key]): + ALL_DEVICE[key][key+str(i+1)] = [] + + for key in i2c_bus: + buses = i2c_bus[key] + nodes = i2c_nodes[key] + for i in range(0,len(buses)): + for j in range(0,len(nodes)): + if 'fan' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ buses[i]+"/fan"+str(k+1)+"_"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + elif 'sfp' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ str(sfp_map[k])+ buses[i]+"/"+ nodes[j] + print "path= %s" %path + print "i=%d" %i + print "k=%d" %k + print "j= %d" %j + print "sfp_map[k]=%s" %sfp_map[k] + print " buses[i]=%s" %buses[i] + print "nodes[j]=%s" %nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + else: + node = key+str(i+1) + path = i2c_prefix+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + + for key in hwmon_types: + itypes = hwmon_types[key] + nodes = hwmon_nodes[key] + for i in range(0,len(itypes)): + for j in range(0,len(nodes)): + node = key+"_"+itypes[i] + path = hwmon_prefix[key]+ itypes[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][ key+str(i+1)].append(path) + + #show dict all in the order + if DEBUG == True: + for i in sorted(ALL_DEVICE.keys()): + print(i+": ") + for j in sorted(ALL_DEVICE[i].keys()): + print(" "+j) + for k in (ALL_DEVICE[i][j]): + print(" "+" "+k) + return + +def show_eeprom(index): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + node = ALL_DEVICE['sfp'] ['sfp'+str(index)][0] + node = node.replace(node.split("/")[-1], 'sfp_eeprom') + # check if got hexdump command in current environment + ret, log = log_os_system("which hexdump", 0) + ret, log2 = log_os_system("which busybox hexdump", 0) + if len(log): + hex_cmd = 'hexdump' + elif len(log2): + hex_cmd = ' busybox hexdump' + else: + log = 'Failed : no hexdump cmd!!' + logging.info(log) + print log + return 1 + + print node + ":" + ret, log = log_os_system("cat "+node+"| "+hex_cmd+" -C", 1) + if ret==0: + print log + else: + print "**********device no found**********" + return + +def set_device(args): + global DEVICE_NO + global ALL_DEVICE + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + + if args[0]=='led': + if int(args[1])>4: + show_set_help() + return + #print ALL_DEVICE['led'] + for i in range(0,len(ALL_DEVICE['led'])): + for k in (ALL_DEVICE['led']['led'+str(i+1)]): + ret, log = log_os_system("echo "+args[1]+" >"+k, 1) + if ret: + return ret + elif args[0]=='fan': + if int(args[1])>100: + show_set_help() + return + #print ALL_DEVICE['fan'] + #fan1~6 is all fine, all fan share same setting + node = ALL_DEVICE['fan1'] ['fan11'][0] + node = node.replace(node.split("/")[-1], 'fan1_duty_cycle_percentage') + ret, log = log_os_system("cat "+ node, 1) + if ret==0: + print ("Previous fan duty: " + log.strip() +"%") + ret, log = log_os_system("echo "+args[1]+" >"+node, 1) + if ret==0: + print ("Current fan duty: " + args[1] +"%") + return ret + elif args[0]=='sfp': + if int(args[1])> DEVICE_NO[args[0]] or int(args[1])==0: + show_set_help() + return + if len(args)<2: + show_set_help() + return + + if int(args[2])>1: + show_set_help() + return + + #print ALL_DEVICE[args[0]] + for i in range(0,len(ALL_DEVICE[args[0]])): + for j in ALL_DEVICE[args[0]][args[0]+str(args[1])]: + if j.find('tx_disable')!= -1: + ret, log = log_os_system("echo "+args[2]+" >"+ j, 1) + if ret: + return ret + + return + +#get digits inside a string. +#Ex: 31 for "sfp31" +def get_value(input): + digit = re.findall('\d+', input) + return int(digit[0]) + +def device_traversal(): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + for i in sorted(ALL_DEVICE.keys()): + print("============================================") + print(i.upper()+": ") + print("============================================") + + for j in sorted(ALL_DEVICE[i].keys(), key=get_value): + print " "+j+":", + for k in (ALL_DEVICE[i][j]): + ret, log = log_os_system("cat "+k, 0) + func = k.split("/")[-1].strip() + func = re.sub(j+'_','',func,1) + func = re.sub(i.lower()+'_','',func,1) + if ret==0: + print func+"="+log+" ", + else: + print func+"="+"X"+" ", + print + print("----------------------------------------------------------------") + + + print + return + +def device_exist(): + ret1, log = log_os_system("ls "+i2c_prefix+"*0060", 0) + ret2, log = log_os_system("ls "+i2c_prefix+"i2c-0", 0) + print(ret1) + print(ret2) + return not(ret1 or ret2) + +if __name__ == "__main__": + main() diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/classes/__init__.py b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/classes/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/classes/fanutil.py b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/classes/fanutil.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/classes/thermalutil.py b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/classes/thermalutil.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/Makefile b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/Makefile new file mode 100644 index 000000000000..1b3477b0b771 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/Makefile @@ -0,0 +1,2 @@ +obj-m:=x86-64-accton-as7816-64x-fan.o x86-64-accton-as7816-64x-sfp.o x86-64-accton-as7816-64x-leds.o \ + x86-64-accton-as7816-64x-psu.o accton_i2c_cpld.o ym2651y.o diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/accton_i2c_cpld.c b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/accton_i2c_cpld.c new file mode 120000 index 000000000000..39c0826d16fd --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/accton_i2c_cpld.c @@ -0,0 +1 @@ +../../common/modules/accton_i2c_cpld.c \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/x86-64-accton-as7816-64x-fan.c b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/x86-64-accton-as7816-64x-fan.c new file mode 100644 index 000000000000..3de2c200e1e1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/x86-64-accton-as7816-64x-fan.c @@ -0,0 +1,466 @@ +/* + * A hwmon driver for the Accton as7816-64x fan + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as7816_64x_fan" + +static struct as7816_64x_fan_data *as7816_64x_fan_update_device(struct device *dev); +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); + +/* fan related data, the index should match sysfs_fan_attributes + */ +static const u8 fan_reg[] = { + 0x80, /* fan 1-4 present status */ + 0x81, /* fan 1-4 direction(0:F2B 1:B2F) */ + 0x87, /* fan PWM(for all fan) */ + 0x90, /* front fan 1 speed(rpm) */ + 0x91, /* front fan 2 speed(rpm) */ + 0x92, /* front fan 3 speed(rpm) */ + 0x93, /* front fan 4 speed(rpm) */ + 0x98, /* rear fan 1 speed(rpm) */ + 0x99, /* rear fan 2 speed(rpm) */ + 0x9A, /* rear fan 3 speed(rpm) */ + 0x9B, /* rear fan 4 speed(rpm) */ +}; + +/* Each client has this additional data */ +struct as7816_64x_fan_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[ARRAY_SIZE(fan_reg)]; /* Register value */ +}; + +enum fan_id { + FAN1_ID, + FAN2_ID, + FAN3_ID, + FAN4_ID +}; + +enum sysfs_fan_attributes { + FAN_PRESENT_REG, + FAN_DIRECTION_REG, + FAN_DUTY_CYCLE_PERCENTAGE, /* Only one CPLD register to control duty cycle for all fans */ + FAN1_FRONT_SPEED_RPM, + FAN2_FRONT_SPEED_RPM, + FAN3_FRONT_SPEED_RPM, + FAN4_FRONT_SPEED_RPM, + FAN1_REAR_SPEED_RPM, + FAN2_REAR_SPEED_RPM, + FAN3_REAR_SPEED_RPM, + FAN4_REAR_SPEED_RPM, + FAN1_DIRECTION, + FAN2_DIRECTION, + FAN3_DIRECTION, + FAN4_DIRECTION, + FAN1_PRESENT, + FAN2_PRESENT, + FAN3_PRESENT, + FAN4_PRESENT, + FAN1_FAULT, + FAN2_FAULT, + FAN3_FAULT, + FAN4_FAULT +}; + +/* Define attributes + */ +#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT);\ + static SENSOR_DEVICE_ATTR(fan##index2##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT) +#define DECLARE_FAN_FAULT_ATTR(index, index2) &sensor_dev_attr_fan##index##_fault.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_fault.dev_attr.attr + +#define DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_direction, S_IRUGO, fan_show_value, NULL, FAN##index##_DIRECTION) +#define DECLARE_FAN_DIRECTION_ATTR(index) &sensor_dev_attr_fan##index##_direction.dev_attr.attr + +#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN_DUTY_CYCLE_PERCENTAGE);\ + static SENSOR_DEVICE_ATTR(pwm##index, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN_DUTY_CYCLE_PERCENTAGE) + +#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) &sensor_dev_attr_fan_duty_cycle_percentage.dev_attr.attr, \ + &sensor_dev_attr_pwm##index.dev_attr.attr + + +#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, fan_show_value, NULL, FAN##index##_PRESENT) +#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr + +#define DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(index, index2) \ + static SENSOR_DEVICE_ATTR(fan##index##_front_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_rear_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index##_input, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ + static SENSOR_DEVICE_ATTR(fan##index2##_input, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM) +#define DECLARE_FAN_SPEED_RPM_ATTR(index, index2) &sensor_dev_attr_fan##index##_front_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_rear_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_input.dev_attr.attr, \ + &sensor_dev_attr_fan##index2##_input.dev_attr.attr + +/* 6 fan fault attributes in this platform */ +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1,11); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2,12); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(3,13); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(4,14); +/* 6 fan speed(rpm) attributes in this platform */ +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1,11); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2,12); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(3,13); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(4,14); +/* 6 fan present attributes in this platform */ +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3); +DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4); +/* 6 fan direction attribute in this platform */ +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(1); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(2); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(3); +DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(4); +/* 1 fan duty cycle attribute in this platform */ +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(1); + +static struct attribute *as7816_64x_fan_attributes[] = { + /* fan related attributes */ + DECLARE_FAN_FAULT_ATTR(1,11), + DECLARE_FAN_FAULT_ATTR(2,12), + DECLARE_FAN_FAULT_ATTR(3,13), + DECLARE_FAN_FAULT_ATTR(4,14), + DECLARE_FAN_SPEED_RPM_ATTR(1,11), + DECLARE_FAN_SPEED_RPM_ATTR(2,12), + DECLARE_FAN_SPEED_RPM_ATTR(3,13), + DECLARE_FAN_SPEED_RPM_ATTR(4,14), + DECLARE_FAN_PRESENT_ATTR(1), + DECLARE_FAN_PRESENT_ATTR(2), + DECLARE_FAN_PRESENT_ATTR(3), + DECLARE_FAN_PRESENT_ATTR(4), + DECLARE_FAN_DIRECTION_ATTR(1), + DECLARE_FAN_DIRECTION_ATTR(2), + DECLARE_FAN_DIRECTION_ATTR(3), + DECLARE_FAN_DIRECTION_ATTR(4), + DECLARE_FAN_DUTY_CYCLE_ATTR(1), + NULL +}; + +#define FAN_DUTY_CYCLE_REG_MASK 0xF +#define FAN_MAX_DUTY_CYCLE 100 +#define FAN_REG_VAL_TO_SPEED_RPM_STEP 100 + +static int as7816_64x_fan_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int as7816_64x_fan_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* fan utility functions + */ +static u32 reg_val_to_duty_cycle(u8 reg_val) +{ + reg_val &= FAN_DUTY_CYCLE_REG_MASK; + + if (!reg_val) { + return 0; + } + + if (reg_val == 0xF) { + return FAN_MAX_DUTY_CYCLE; + } + + return (reg_val * 6) + 10; +} + +static u8 duty_cycle_to_reg_val(u8 duty_cycle) +{ + if (duty_cycle < 16) { + return 0; + } + + if (duty_cycle >= 100) { + return 0xF; + } + + return (duty_cycle - 10) / 6; +} + +static u32 reg_val_to_speed_rpm(u8 reg_val) +{ + return (u32)reg_val * FAN_REG_VAL_TO_SPEED_RPM_STEP; +} + +static u8 reg_val_to_direction(u8 reg_val, enum fan_id id) +{ + u8 mask = (1 << id); + return !!(reg_val & mask); +} + +static u8 reg_val_to_is_present(u8 reg_val, enum fan_id id) +{ + u8 mask = (1 << id); + return !(reg_val & mask); +} + +static u8 is_fan_fault(struct as7816_64x_fan_data *data, enum fan_id id) +{ + u8 ret = 1; + int front_fan_index = FAN1_FRONT_SPEED_RPM + id; + int rear_fan_index = FAN1_REAR_SPEED_RPM + id; + + /* Check if the speed of front or rear fan is ZERO, + */ + if (reg_val_to_speed_rpm(data->reg_val[front_fan_index]) && + reg_val_to_speed_rpm(data->reg_val[rear_fan_index])) { + ret = 0; + } + + return ret; +} + +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int error, value; + struct i2c_client *client = to_i2c_client(dev); + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < 0 || value > FAN_MAX_DUTY_CYCLE) + return -EINVAL; + + as7816_64x_fan_write_value(client, 0x28, 0); /* Disable fan speed watch dog */ + as7816_64x_fan_write_value(client, fan_reg[FAN_DUTY_CYCLE_PERCENTAGE], duty_cycle_to_reg_val(value)); + return count; +} + +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7816_64x_fan_data *data = as7816_64x_fan_update_device(dev); + ssize_t ret = 0; + + if (data->valid) { + switch (attr->index) { + case FAN_DUTY_CYCLE_PERCENTAGE: + { + u32 duty_cycle = reg_val_to_duty_cycle(data->reg_val[FAN_DUTY_CYCLE_PERCENTAGE]); + ret = sprintf(buf, "%u\n", duty_cycle); + break; + } + case FAN1_FRONT_SPEED_RPM: + case FAN2_FRONT_SPEED_RPM: + case FAN3_FRONT_SPEED_RPM: + case FAN4_FRONT_SPEED_RPM: + case FAN1_REAR_SPEED_RPM: + case FAN2_REAR_SPEED_RPM: + case FAN3_REAR_SPEED_RPM: + case FAN4_REAR_SPEED_RPM: + { + ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index])); + break; + } + case FAN1_PRESENT: + case FAN2_PRESENT: + case FAN3_PRESENT: + case FAN4_PRESENT: + ret = sprintf(buf, "%d\n", + reg_val_to_is_present(data->reg_val[FAN_PRESENT_REG], + attr->index - FAN1_PRESENT)); + break; + case FAN1_FAULT: + case FAN2_FAULT: + case FAN3_FAULT: + case FAN4_FAULT: + ret = sprintf(buf, "%d\n", is_fan_fault(data, attr->index - FAN1_FAULT)); + break; + case FAN1_DIRECTION: + case FAN2_DIRECTION: + case FAN3_DIRECTION: + case FAN4_DIRECTION: + ret = sprintf(buf, "%d\n", + reg_val_to_direction(data->reg_val[FAN_DIRECTION_REG], + attr->index - FAN1_DIRECTION)); + break; + default: + break; + } + } + + return ret; +} + +static const struct attribute_group as7816_64x_fan_group = { + .attrs = as7816_64x_fan_attributes, +}; + +static struct as7816_64x_fan_data *as7816_64x_fan_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7816_64x_fan_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || + !data->valid) { + int i; + + dev_dbg(&client->dev, "Starting as7816_64x_fan update\n"); + data->valid = 0; + + /* Update fan data + */ + for (i = 0; i < ARRAY_SIZE(data->reg_val); i++) { + int status = as7816_64x_fan_read_value(client, fan_reg[i]); + + if (status < 0) { + data->valid = 0; + mutex_unlock(&data->update_lock); + dev_dbg(&client->dev, "reg %d, err %d\n", fan_reg[i], status); + return data; + } + else { + data->reg_val[i] = status; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int as7816_64x_fan_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7816_64x_fan_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7816_64x_fan_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, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7816_64x_fan_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: fan '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7816_64x_fan_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7816_64x_fan_remove(struct i2c_client *client) +{ + struct as7816_64x_fan_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7816_64x_fan_group); + + return 0; +} + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +static const struct i2c_device_id as7816_64x_fan_id[] = { + { "as7816_64x_fan", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7816_64x_fan_id); + +static struct i2c_driver as7816_64x_fan_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .probe = as7816_64x_fan_probe, + .remove = as7816_64x_fan_remove, + .id_table = as7816_64x_fan_id, + .address_list = normal_i2c, +}; + +static int __init as7816_64x_fan_init(void) +{ + return i2c_add_driver(&as7816_64x_fan_driver); +} + +static void __exit as7816_64x_fan_exit(void) +{ + i2c_del_driver(&as7816_64x_fan_driver); +} + +module_init(as7816_64x_fan_init); +module_exit(as7816_64x_fan_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7816_64x_fan driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/x86-64-accton-as7816-64x-leds.c b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/x86-64-accton-as7816-64x-leds.c new file mode 100644 index 000000000000..025f87d692a3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/x86-64-accton-as7816-64x-leds.c @@ -0,0 +1,460 @@ +/* + * A LED driver for the as7816_64x_led + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * 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. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern int accton_i2c_cpld_read (u8 cpld_addr, u8 reg); +extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +extern void led_classdev_unregister(struct led_classdev *led_cdev); +extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev); +extern void led_classdev_resume(struct led_classdev *led_cdev); +extern void led_classdev_suspend(struct led_classdev *led_cdev); + +#define DRVNAME "as7816_64x_led" + +struct as7816_64x_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[1]; /* only 1 register*/ +}; + +static struct as7816_64x_led_data *ledctl = NULL; + +/* LED related data + */ + +#define LED_CNTRLER_I2C_ADDRESS (0x60) + +#define LED_TYPE_DIAG_REG_MASK (0x03) +#define LED_MODE_DIAG_YELLOW_VALUE (0x00) +#define LED_MODE_DIAG_RED_VALUE (0x01) +#define LED_MODE_DIAG_GREEN_VALUE (0x02) +#define LED_MODE_DIAG_OFF_VALUE (0x03) + +#define LED_TYPE_LOC_REG_MASK (0x10) +#define LED_MODE_LOC_ORANGE_VALUE (0x00) +#define LED_MODE_LOC_OFF_VALUE (0x10) + +#define LED_TYPE_FAN_REG_MASK (0x0C) +#define LED_MODE_FAN_ORANGE_VALUE (0x04) +#define LED_MODE_FAN_GREEN_VALUE_1 (0x00) +#define LED_MODE_FAN_GREEN_VALUE_2 (0x08) +#define LED_MODE_FAN_OFF_VALUE (0x0C) + +enum led_type { + LED_TYPE_DIAG, + LED_TYPE_LOC, + LED_TYPE_FAN, + LED_TYPE_PSU1, + LED_TYPE_PSU2 +}; + +struct led_reg { + u32 types; + u8 reg_addr; +}; + +static const struct led_reg led_reg_map[] = { + {(1 << LED_TYPE_LOC) | (1 << LED_TYPE_DIAG) | (1 << LED_TYPE_FAN), 0x30}, +}; + +enum led_light_mode { + LED_MODE_OFF, + LED_MODE_RED = 10, + LED_MODE_RED_BLINKING = 11, + LED_MODE_ORANGE = 12, + LED_MODE_ORANGE_BLINKING = 13, + LED_MODE_YELLOW = 14, + LED_MODE_YELLOW_BLINKING = 15, + LED_MODE_GREEN = 16, + LED_MODE_GREEN_BLINKING = 17, + LED_MODE_BLUE = 18, + LED_MODE_BLUE_BLINKING = 19, + LED_MODE_PURPLE = 20, + LED_MODE_PURPLE_BLINKING = 21, + LED_MODE_AUTO = 22, + LED_MODE_AUTO_BLINKING = 23, + LED_MODE_WHITE = 24, + LED_MODE_WHITE_BLINKING = 25, + LED_MODE_CYAN = 26, + LED_MODE_CYAN_BLINKING = 27, + LED_MODE_UNKNOWN = 99 +}; + +struct led_type_mode { + enum led_type type; + enum led_light_mode mode; + int reg_bit_mask; + int mode_value; +}; + +static struct led_type_mode led_type_mode_data[] = { +{LED_TYPE_LOC, LED_MODE_OFF, LED_TYPE_LOC_REG_MASK, LED_MODE_LOC_OFF_VALUE}, +{LED_TYPE_LOC, LED_MODE_ORANGE,LED_TYPE_LOC_REG_MASK, LED_MODE_LOC_ORANGE_VALUE}, +{LED_TYPE_DIAG, LED_MODE_OFF, LED_TYPE_DIAG_REG_MASK, LED_MODE_DIAG_OFF_VALUE}, +{LED_TYPE_DIAG, LED_MODE_GREEN, LED_TYPE_DIAG_REG_MASK, LED_MODE_DIAG_GREEN_VALUE}, +{LED_TYPE_DIAG, LED_MODE_RED, LED_TYPE_DIAG_REG_MASK, LED_MODE_DIAG_RED_VALUE}, +{LED_TYPE_DIAG, LED_MODE_YELLOW,LED_TYPE_DIAG_REG_MASK, LED_MODE_DIAG_YELLOW_VALUE}, +{LED_TYPE_FAN, LED_MODE_OFF, LED_TYPE_FAN_REG_MASK, LED_MODE_FAN_OFF_VALUE}, +{LED_TYPE_FAN, LED_MODE_GREEN, LED_TYPE_FAN_REG_MASK, LED_MODE_FAN_GREEN_VALUE_1}, +{LED_TYPE_FAN, LED_MODE_GREEN, LED_TYPE_FAN_REG_MASK, LED_MODE_FAN_GREEN_VALUE_2}, +{LED_TYPE_FAN, LED_MODE_ORANGE,LED_TYPE_FAN_REG_MASK, LED_MODE_FAN_ORANGE_VALUE} +}; + +static int get_led_reg(enum led_type type, u8 *reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(led_reg_map); i++) { + if(led_reg_map[i].types & (1 << type)) { + *reg = led_reg_map[i].reg_addr; + return 0; + } + } + + return 1; +} + +static int led_reg_val_to_light_mode(enum led_type type, u8 reg_val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(led_type_mode_data); i++) { + + if (type != led_type_mode_data[i].type) + continue; + + if ((led_type_mode_data[i].reg_bit_mask & reg_val) == + led_type_mode_data[i].mode_value) + { + return led_type_mode_data[i].mode; + } + } + + return 0; +} + +static u8 led_light_mode_to_reg_val(enum led_type type, + enum led_light_mode mode, u8 reg_val) { + int i; + + for (i = 0; i < ARRAY_SIZE(led_type_mode_data); i++) { + if (type != led_type_mode_data[i].type) + continue; + + if (mode != led_type_mode_data[i].mode) + continue; + + reg_val = led_type_mode_data[i].mode_value | + (reg_val & (~led_type_mode_data[i].reg_bit_mask)); + break; + } + + return reg_val; +} + +static int as7816_64x_led_read_value(u8 reg) +{ + return accton_i2c_cpld_read(LED_CNTRLER_I2C_ADDRESS, reg); +} + +static int as7816_64x_led_write_value(u8 reg, u8 value) +{ + return accton_i2c_cpld_write(LED_CNTRLER_I2C_ADDRESS, reg, value); +} + +static void as7816_64x_led_update(void) +{ + mutex_lock(&ledctl->update_lock); + + if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) + || !ledctl->valid) { + int i; + + dev_dbg(&ledctl->pdev->dev, "Starting as7816_64x_led update\n"); + + /* Update LED data + */ + for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { + int status = as7816_64x_led_read_value(led_reg_map[i].reg_addr); + + if (status < 0) { + ledctl->valid = 0; + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg_map[i].reg_addr, status); + goto exit; + } + else + { + ledctl->reg_val[i] = status; + } + } + + ledctl->last_updated = jiffies; + ledctl->valid = 1; + } + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void as7816_64x_led_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode, + enum led_type type) +{ + int reg_val; + u8 reg ; + mutex_lock(&ledctl->update_lock); + + if( !get_led_reg(type, ®)) { + dev_dbg(&ledctl->pdev->dev, "Not match register for %d.\n", type); + } + + reg_val = as7816_64x_led_read_value(reg); + if (reg_val < 0) { + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", reg, reg_val); + goto exit; + } + + reg_val = led_light_mode_to_reg_val(type, led_light_mode, reg_val); + as7816_64x_led_write_value(reg, reg_val); + + /* to prevent the slow-update issue */ + ledctl->valid = 0; + +exit: + mutex_unlock(&ledctl->update_lock); +} + + +static void as7816_64x_led_diag_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + as7816_64x_led_set(led_cdev, led_light_mode, LED_TYPE_DIAG); +} + +static enum led_brightness as7816_64x_led_diag_get(struct led_classdev *cdev) +{ + as7816_64x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); +} + +static void as7816_64x_led_loc_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + as7816_64x_led_set(led_cdev, led_light_mode, LED_TYPE_LOC); +} + +static enum led_brightness as7816_64x_led_loc_get(struct led_classdev *cdev) +{ + as7816_64x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); +} + +static void as7816_64x_led_fan_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + as7816_64x_led_set(led_cdev, led_light_mode, LED_TYPE_FAN); +} + +static enum led_brightness as7816_64x_led_fan_get(struct led_classdev *cdev) +{ + as7816_64x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_FAN, ledctl->reg_val[0]); +} + +static void as7816_64x_led_auto_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ +} + +static enum led_brightness as7816_64x_led_auto_get(struct led_classdev *cdev) +{ + return LED_MODE_AUTO; +} + +static struct led_classdev as7816_64x_leds[] = { + [LED_TYPE_DIAG] = { + .name = "as7816_64x_led::diag", + .default_trigger = "unused", + .brightness_set = as7816_64x_led_diag_set, + .brightness_get = as7816_64x_led_diag_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_GREEN, + }, + [LED_TYPE_LOC] = { + .name = "as7816_64x_led::loc", + .default_trigger = "unused", + .brightness_set = as7816_64x_led_loc_set, + .brightness_get = as7816_64x_led_loc_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_ORANGE, + }, + [LED_TYPE_FAN] = { + .name = "as7816_64x_led::fan", + .default_trigger = "unused", + .brightness_set = as7816_64x_led_fan_set, + .brightness_get = as7816_64x_led_fan_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_GREEN, + }, + [LED_TYPE_PSU1] = { + .name = "as7816_64x_led::psu1", + .default_trigger = "unused", + .brightness_set = as7816_64x_led_auto_set, + .brightness_get = as7816_64x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU2] = { + .name = "as7816_64x_led::psu2", + .default_trigger = "unused", + .brightness_set = as7816_64x_led_auto_set, + .brightness_get = as7816_64x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, +}; + +static int as7816_64x_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(as7816_64x_leds); i++) { + led_classdev_suspend(&as7816_64x_leds[i]); + } + + return 0; +} + +static int as7816_64x_led_resume(struct platform_device *dev) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(as7816_64x_leds); i++) { + led_classdev_resume(&as7816_64x_leds[i]); + } + + return 0; +} + +static int as7816_64x_led_probe(struct platform_device *pdev) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(as7816_64x_leds); i++) { + ret = led_classdev_register(&pdev->dev, &as7816_64x_leds[i]); + + if (ret < 0) + break; + } + + /* Check if all LEDs were successfully registered */ + if (i != ARRAY_SIZE(as7816_64x_leds)){ + int j; + + /* only unregister the LEDs that were successfully registered */ + for (j = 0; j < i; j++) { + led_classdev_unregister(&as7816_64x_leds[i]); + } + } + + return ret; +} + +static int as7816_64x_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(as7816_64x_leds); i++) { + led_classdev_unregister(&as7816_64x_leds[i]); + } + + return 0; +} + +static struct platform_driver as7816_64x_led_driver = { + .probe = as7816_64x_led_probe, + .remove = as7816_64x_led_remove, + .suspend = as7816_64x_led_suspend, + .resume = as7816_64x_led_resume, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init as7816_64x_led_init(void) +{ + int ret; + + ret = platform_driver_register(&as7816_64x_led_driver); + if (ret < 0) { + goto exit; + } + + ledctl = kzalloc(sizeof(struct as7816_64x_led_data), GFP_KERNEL); + if (!ledctl) { + ret = -ENOMEM; + platform_driver_unregister(&as7816_64x_led_driver); + goto exit; + } + + mutex_init(&ledctl->update_lock); + + ledctl->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(ledctl->pdev)) { + ret = PTR_ERR(ledctl->pdev); + platform_driver_unregister(&as7816_64x_led_driver); + kfree(ledctl); + goto exit; + } + +exit: + return ret; +} + +static void __exit as7816_64x_led_exit(void) +{ + platform_device_unregister(ledctl->pdev); + platform_driver_unregister(&as7816_64x_led_driver); + kfree(ledctl); +} + +module_init(as7816_64x_led_init); +module_exit(as7816_64x_led_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7816_64x_led driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/x86-64-accton-as7816-64x-psu.c b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/x86-64-accton-as7816-64x-psu.c new file mode 100644 index 000000000000..cdc535347dc0 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/x86-64-accton-as7816-64x-psu.c @@ -0,0 +1,239 @@ +/* + * An hwmon driver for accton as7816_64x Power Module + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PSU_STATUS_I2C_ADDR 0x60 +#define PSU_STATUS_I2C_REG_OFFSET 0x03 + +#define IS_POWER_GOOD(id, value) (!!(value & BIT(2+id))) +#define IS_PRESENT(id, value) (!(value & BIT(id))) + +static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); +static struct as7816_64x_psu_data *as7816_64x_psu_update_device(struct device *dev); +extern int accton_i2c_cpld_read (u8 cpld_addr, u8 reg); + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as7816_64x_psu_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + u8 status; /* Status(present/power_good) register read from CPLD */ +}; + +enum as7816_64x_psu_sysfs_attributes { + PSU_PRESENT, + PSU_POWER_GOOD +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_status, NULL, PSU_PRESENT); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD); + +static struct attribute *as7816_64x_psu_attributes[] = { + &sensor_dev_attr_psu_present.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + NULL +}; + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7816_64x_psu_data *data = as7816_64x_psu_update_device(dev); + u8 status = 0; + + if (!data->valid) { + return -EIO; + } + + if (attr->index == PSU_PRESENT) { + status = IS_PRESENT(data->index, data->status); + } + else { /* PSU_POWER_GOOD */ + status = IS_POWER_GOOD(data->index, data->status); + } + + return sprintf(buf, "%d\n", status); +} + +static const struct attribute_group as7816_64x_psu_group = { + .attrs = as7816_64x_psu_attributes, +}; + +static int as7816_64x_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as7816_64x_psu_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as7816_64x_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7816_64x_psu_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7816_64x_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as7816_64x_psu_remove(struct i2c_client *client) +{ + struct as7816_64x_psu_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7816_64x_psu_group); + kfree(data); + + return 0; +} + +enum psu_index +{ + as7816_64x_psu1, + as7816_64x_psu2 +}; + +static const struct i2c_device_id as7816_64x_psu_id[] = { + { "as7816_64x_psu1", as7816_64x_psu1 }, + { "as7816_64x_psu2", as7816_64x_psu2 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as7816_64x_psu_id); + +static struct i2c_driver as7816_64x_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as7816_64x_psu", + }, + .probe = as7816_64x_psu_probe, + .remove = as7816_64x_psu_remove, + .id_table = as7816_64x_psu_id, + .address_list = normal_i2c, +}; + +static struct as7816_64x_psu_data *as7816_64x_psu_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7816_64x_psu_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int status; + + data->valid = 0; + dev_dbg(&client->dev, "Starting as7816_64x update\n"); + + /* Read psu status */ + status = accton_i2c_cpld_read(PSU_STATUS_I2C_ADDR, PSU_STATUS_I2C_REG_OFFSET); + + if (status < 0) { + dev_dbg(&client->dev, "cpld reg (0x%x) err %d\n", PSU_STATUS_I2C_ADDR, status); + goto exit; + } + else { + data->status = status; + } + + data->last_updated = jiffies; + data->valid = 1; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init as7816_64x_psu_init(void) +{ + return i2c_add_driver(&as7816_64x_psu_driver); +} + +static void __exit as7816_64x_psu_exit(void) +{ + i2c_del_driver(&as7816_64x_psu_driver); +} + +module_init(as7816_64x_psu_init); +module_exit(as7816_64x_psu_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as7816_64x_psu driver"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/x86-64-accton-as7816-64x-sfp.c b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/x86-64-accton-as7816-64x-sfp.c new file mode 100644 index 000000000000..76444ebbafcb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/x86-64-accton-as7816-64x-sfp.c @@ -0,0 +1,1576 @@ +/* + * SFP driver for accton as7816_64x sfp + * + * Copyright (C) Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "as7816_64x_sfp" /* Platform dependent */ + +#define DEBUG_MODE 0 + +#if (DEBUG_MODE == 1) + #define DEBUG_PRINT(fmt, args...) \ + printk (KERN_INFO "%s:%s[%d]: " fmt "\r\n", __FILE__, __FUNCTION__, __LINE__, ##args) +#else + #define DEBUG_PRINT(fmt, args...) +#endif + +#define NUM_OF_SFP_PORT 24 +#define EEPROM_NAME "sfp_eeprom" +#define EEPROM_SIZE 256 /* 256 byte eeprom */ +#define BIT_INDEX(i) (1ULL << (i)) +#define USE_I2C_BLOCK_READ 1 /* Platform dependent */ +#define I2C_RW_RETRY_COUNT 10 +#define I2C_RW_RETRY_INTERVAL 60 /* ms */ + +#define SFP_EEPROM_A0_I2C_ADDR (0xA0 >> 1) + +#define SFF8024_PHYSICAL_DEVICE_ID_ADDR 0x0 +#define SFF8024_DEVICE_ID_SFP 0x3 +#define SFF8024_DEVICE_ID_QSFP 0xC +#define SFF8024_DEVICE_ID_QSFP_PLUS 0xD +#define SFF8024_DEVICE_ID_QSFP28 0x11 + +#define SFF8436_RX_LOS_ADDR 3 +#define SFF8436_TX_FAULT_ADDR 4 +#define SFF8436_TX_DISABLE_ADDR 86 + +#define MULTIPAGE_SUPPORT 1 + +#if (MULTIPAGE_SUPPORT == 1) +/* fundamental unit of addressing for SFF_8472/SFF_8436 */ +#define SFF_8436_PAGE_SIZE 128 +/* + * The current 8436 (QSFP) spec provides for only 4 supported + * pages (pages 0-3). + * This driver is prepared to support more, but needs a register in the + * EEPROM to indicate how many pages are supported before it is safe + * to implement more pages in the driver. + */ +#define SFF_8436_SPECED_PAGES 4 +#define SFF_8436_EEPROM_SIZE ((1 + SFF_8436_SPECED_PAGES) * SFF_8436_PAGE_SIZE) +#define SFF_8436_EEPROM_UNPAGED_SIZE (2 * SFF_8436_PAGE_SIZE) +/* + * The current 8472 (SFP) spec provides for only 3 supported + * pages (pages 0-2). + * This driver is prepared to support more, but needs a register in the + * EEPROM to indicate how many pages are supported before it is safe + * to implement more pages in the driver. + */ +#define SFF_8472_SPECED_PAGES 3 +#define SFF_8472_EEPROM_SIZE ((3 + SFF_8472_SPECED_PAGES) * SFF_8436_PAGE_SIZE) +#define SFF_8472_EEPROM_UNPAGED_SIZE (4 * SFF_8436_PAGE_SIZE) + +/* a few constants to find our way around the EEPROM */ +#define SFF_8436_PAGE_SELECT_REG 0x7F +#define SFF_8436_PAGEABLE_REG 0x02 +#define SFF_8436_NOT_PAGEABLE (1<<2) +#define SFF_8472_PAGEABLE_REG 0x40 +#define SFF_8472_PAGEABLE (1<<4) + +/* + * 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 = SFF_8436_PAGE_SIZE; + +/* + * 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; + +typedef enum qsfp_opcode { + QSFP_READ_OP = 0, + QSFP_WRITE_OP = 1 +} qsfp_opcode_e; +#endif + +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_present(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t qsfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t qsfp_set_tx_disable(struct device *dev, struct device_attribute *da, const char *buf, size_t count);; +static ssize_t sfp_eeprom_read(struct i2c_client *, u8, u8 *,int); +static ssize_t sfp_eeprom_write(struct i2c_client *, u8 , const char *,int); +extern int accton_i2c_cpld_read (u8 cpld_addr, u8 reg); + +enum sfp_sysfs_attributes { + PRESENT, + PRESENT_ALL, + PORT_NUMBER, + PORT_TYPE, + DDM_IMPLEMENTED, + TX_FAULT, + TX_FAULT1, + TX_FAULT2, + TX_FAULT3, + TX_FAULT4, + TX_DISABLE, + TX_DISABLE1, + TX_DISABLE2, + TX_DISABLE3, + TX_DISABLE4, + RX_LOS, + RX_LOS1, + RX_LOS2, + RX_LOS3, + RX_LOS4, + RX_LOS_ALL +}; + +/* SFP/QSFP common attributes for sysfs */ +static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, PORT_NUMBER); +static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_present, NULL, PRESENT); +static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_present, NULL, PRESENT_ALL); +static SENSOR_DEVICE_ATTR(sfp_tx_disable, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE); +static SENSOR_DEVICE_ATTR(sfp_tx_fault, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT); + +/* QSFP attributes for sysfs */ +static SENSOR_DEVICE_ATTR(sfp_rx_los, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS); +static SENSOR_DEVICE_ATTR(sfp_rx_los1, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS1); +static SENSOR_DEVICE_ATTR(sfp_rx_los2, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS2); +static SENSOR_DEVICE_ATTR(sfp_rx_los3, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS3); +static SENSOR_DEVICE_ATTR(sfp_rx_los4, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS4); +static SENSOR_DEVICE_ATTR(sfp_tx_disable1, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE1); +static SENSOR_DEVICE_ATTR(sfp_tx_disable2, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE2); +static SENSOR_DEVICE_ATTR(sfp_tx_disable3, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE3); +static SENSOR_DEVICE_ATTR(sfp_tx_disable4, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE4); +static SENSOR_DEVICE_ATTR(sfp_tx_fault1, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT1); +static SENSOR_DEVICE_ATTR(sfp_tx_fault2, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT2); +static SENSOR_DEVICE_ATTR(sfp_tx_fault3, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT3); +static SENSOR_DEVICE_ATTR(sfp_tx_fault4, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT4); +static struct attribute *qsfp_attributes[] = { + &sensor_dev_attr_sfp_port_number.dev_attr.attr, + &sensor_dev_attr_sfp_is_present.dev_attr.attr, + &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los1.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los2.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los3.dev_attr.attr, + &sensor_dev_attr_sfp_rx_los4.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable1.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable2.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable3.dev_attr.attr, + &sensor_dev_attr_sfp_tx_disable4.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault1.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault2.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault3.dev_attr.attr, + &sensor_dev_attr_sfp_tx_fault4.dev_attr.attr, + NULL +}; + +/* Platform dependent +++ */ +#define CPLD_PORT_TO_FRONT_PORT(port) (port+1) + +enum port_numbers { +as7816_64x_port1, as7816_64x_port2, as7816_64x_port3, as7816_64x_port4, +as7816_64x_port5, as7816_64x_port6, as7816_64x_port7, as7816_64x_port8, +as7816_64x_port9, as7816_64x_port10, as7816_64x_port11, as7816_64x_port12, +as7816_64x_port13, as7816_64x_port14, as7816_64x_port15, as7816_64x_port16, +as7816_64x_port17, as7816_64x_port18, as7816_64x_port19, as7816_64x_port20, +as7816_64x_port21, as7816_64x_port22, as7816_64x_port23, as7816_64x_port24, +as7816_64x_port25, as7816_64x_port26, as7816_64x_port27, as7816_64x_port28, +as7816_64x_port29, as7816_64x_port30, as7816_64x_port31, as7816_64x_port32, +as7816_64x_port33, as7816_64x_port34, as7816_64x_port35, as7816_64x_port36, +as7816_64x_port37, as7816_64x_port38, as7816_64x_port39, as7816_64x_port40, +as7816_64x_port41, as7816_64x_port42, as7816_64x_port43, as7816_64x_port44, +as7816_64x_port45, as7816_64x_port46, as7816_64x_port47, as7816_64x_port48, +as7816_64x_port49, as7816_64x_port50, as7816_64x_port51, as7816_64x_port52, +as7816_64x_port53, as7816_64x_port54, as7816_64x_port55, as7816_64x_port56, +as7816_64x_port57, as7816_64x_port58, as7816_64x_port59, as7816_64x_port60, +as7816_64x_port61, as7816_64x_port62, as7816_64x_port63, as7816_64x_port64 +}; + +#define I2C_DEV_ID(x) { #x, x} + +static const struct i2c_device_id sfp_device_id[] = { +I2C_DEV_ID(as7816_64x_port1), +I2C_DEV_ID(as7816_64x_port2), +I2C_DEV_ID(as7816_64x_port3), +I2C_DEV_ID(as7816_64x_port4), +I2C_DEV_ID(as7816_64x_port5), +I2C_DEV_ID(as7816_64x_port6), +I2C_DEV_ID(as7816_64x_port7), +I2C_DEV_ID(as7816_64x_port8), +I2C_DEV_ID(as7816_64x_port9), +I2C_DEV_ID(as7816_64x_port10), +I2C_DEV_ID(as7816_64x_port11), +I2C_DEV_ID(as7816_64x_port12), +I2C_DEV_ID(as7816_64x_port13), +I2C_DEV_ID(as7816_64x_port14), +I2C_DEV_ID(as7816_64x_port15), +I2C_DEV_ID(as7816_64x_port16), +I2C_DEV_ID(as7816_64x_port17), +I2C_DEV_ID(as7816_64x_port18), +I2C_DEV_ID(as7816_64x_port19), +I2C_DEV_ID(as7816_64x_port20), +I2C_DEV_ID(as7816_64x_port21), +I2C_DEV_ID(as7816_64x_port22), +I2C_DEV_ID(as7816_64x_port23), +I2C_DEV_ID(as7816_64x_port24), +I2C_DEV_ID(as7816_64x_port25), +I2C_DEV_ID(as7816_64x_port26), +I2C_DEV_ID(as7816_64x_port27), +I2C_DEV_ID(as7816_64x_port28), +I2C_DEV_ID(as7816_64x_port29), +I2C_DEV_ID(as7816_64x_port30), +I2C_DEV_ID(as7816_64x_port31), +I2C_DEV_ID(as7816_64x_port32), +I2C_DEV_ID(as7816_64x_port33), +I2C_DEV_ID(as7816_64x_port34), +I2C_DEV_ID(as7816_64x_port35), +I2C_DEV_ID(as7816_64x_port36), +I2C_DEV_ID(as7816_64x_port37), +I2C_DEV_ID(as7816_64x_port38), +I2C_DEV_ID(as7816_64x_port39), +I2C_DEV_ID(as7816_64x_port40), +I2C_DEV_ID(as7816_64x_port41), +I2C_DEV_ID(as7816_64x_port42), +I2C_DEV_ID(as7816_64x_port43), +I2C_DEV_ID(as7816_64x_port44), +I2C_DEV_ID(as7816_64x_port45), +I2C_DEV_ID(as7816_64x_port46), +I2C_DEV_ID(as7816_64x_port47), +I2C_DEV_ID(as7816_64x_port48), +I2C_DEV_ID(as7816_64x_port49), +I2C_DEV_ID(as7816_64x_port50), +I2C_DEV_ID(as7816_64x_port51), +I2C_DEV_ID(as7816_64x_port52), +I2C_DEV_ID(as7816_64x_port53), +I2C_DEV_ID(as7816_64x_port54), +I2C_DEV_ID(as7816_64x_port55), +I2C_DEV_ID(as7816_64x_port56), +I2C_DEV_ID(as7816_64x_port57), +I2C_DEV_ID(as7816_64x_port58), +I2C_DEV_ID(as7816_64x_port59), +I2C_DEV_ID(as7816_64x_port60), +I2C_DEV_ID(as7816_64x_port61), +I2C_DEV_ID(as7816_64x_port62), +I2C_DEV_ID(as7816_64x_port63), +I2C_DEV_ID(as7816_64x_port64), +{ /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, sfp_device_id); +/* Platform dependent --- */ + +enum driver_type_e { + DRIVER_TYPE_SFP_MSA, + DRIVER_TYPE_SFP_DDM, + DRIVER_TYPE_QSFP, + DRIVER_TYPE_XFP +}; + +/* Each client has this additional data + */ +struct eeprom_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + struct bin_attribute bin; /* eeprom data */ +}; + +struct qsfp_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 status[3]; /* bit0:port0, bit1:port1 and so on */ + /* index 0 => tx_fail + 1 => tx_disable + 2 => rx_loss */ + u8 device_id; + struct eeprom_data eeprom; +}; + +struct sfp_port_data { + struct mutex update_lock; + enum driver_type_e driver_type; + int port; /* CPLD port index */ + u64 present; /* present status, bit0:port0, bit1:port1 and so on */ + + struct qsfp_data *qsfp; + + struct i2c_client *client; +#if (MULTIPAGE_SUPPORT == 1) + int use_smbus; + u8 *writebuf; + unsigned write_max; +#endif +}; + +#if (MULTIPAGE_SUPPORT == 1) +static ssize_t sfp_port_read_write(struct sfp_port_data *port_data, + char *buf, loff_t off, size_t len, qsfp_opcode_e opcode); +#endif +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + return sprintf(buf, "%d\n", CPLD_PORT_TO_FRONT_PORT(data->port)); +} + +/* Platform dependent +++ */ +static struct sfp_port_data *sfp_update_present(struct i2c_client *client) +{ + struct sfp_port_data *data = i2c_get_clientdata(client); + int i = 0; + int status = -1; + u8 regs[] = {0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77}; + + DEBUG_PRINT("Starting sfp present status update"); + mutex_lock(&data->update_lock); + + /* Read present status of port 1~64 */ + data->present = 0; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + status = accton_i2c_cpld_read(0x60, regs[i]); + + if (status < 0) { + DEBUG_PRINT("cpld(0x60) reg(0x%x) err %d", regs[i], status); + goto exit; + } + + DEBUG_PRINT("Present status = 0x%lx", data->present); + data->present |= (u64)status << (i*8); + } + + DEBUG_PRINT("Present status = 0x%lx", data->present); +exit: + mutex_unlock(&data->update_lock); + return (status < 0) ? ERR_PTR(status) : data; +} + +/* Platform dependent --- */ + +static int sfp_is_port_present(struct i2c_client *client, int port) +{ + struct sfp_port_data *data = i2c_get_clientdata(client); + + data = sfp_update_present(client); + if (IS_ERR(data)) { + return PTR_ERR(data); + } + + return (data->present & BIT_INDEX(data->port)) ? 0 : 1; /* Platform dependent */ +} + +/* Platform dependent +++ */ +static ssize_t show_present(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + + if (PRESENT_ALL == attr->index) { + int i; + u8 values[8] = {0}; + struct sfp_port_data *data = sfp_update_present(client); + + if (IS_ERR(data)) { + return PTR_ERR(data); + } + + for (i = 0; i < ARRAY_SIZE(values); i++) { + values[i] = ~(u8)(data->present >> (i * 8)); + } + + /* Return values 1 -> 64 in order */ + return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], values[3], + values[4], values[5], values[6], values[7]); + } + else { + struct sfp_port_data *data = i2c_get_clientdata(client); + int present = sfp_is_port_present(client, data->port); + + if (IS_ERR_VALUE(present)) { + return present; + } + + /* PRESENT */ + return sprintf(buf, "%d\n", present); + } +} +/* Platform dependent --- */ + +static struct sfp_port_data *qsfp_update_tx_rx_status(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + int i, status = -1; + u8 buf = 0; + u8 reg[] = {SFF8436_TX_FAULT_ADDR, SFF8436_TX_DISABLE_ADDR, SFF8436_RX_LOS_ADDR}; + + if (time_before(jiffies, data->qsfp->last_updated + HZ + HZ / 2) && data->qsfp->valid) { + return data; + } + + DEBUG_PRINT("Starting sfp tx rx status update"); + mutex_lock(&data->update_lock); + data->qsfp->valid = 0; + memset(data->qsfp->status, 0, sizeof(data->qsfp->status)); + + /* Notify device to update tx fault/ tx disable/ rx los status */ + for (i = 0; i < ARRAY_SIZE(reg); i++) { + status = sfp_eeprom_read(client, reg[i], &buf, sizeof(buf)); + if (unlikely(status < 0)) { + goto exit; + } + } + msleep(200); + + /* Read actual tx fault/ tx disable/ rx los status */ + for (i = 0; i < ARRAY_SIZE(reg); i++) { + status = sfp_eeprom_read(client, reg[i], &buf, sizeof(buf)); + if (unlikely(status < 0)) { + goto exit; + } + + DEBUG_PRINT("qsfp reg(0x%x) status = (0x%x)", reg[i], data->qsfp->status[i]); + data->qsfp->status[i] = (buf & 0xF); + } + + data->qsfp->valid = 1; + data->qsfp->last_updated = jiffies; + +exit: + mutex_unlock(&data->update_lock); + return (status < 0) ? ERR_PTR(status) : data; +} + +static ssize_t qsfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + int present; + u8 val = 0; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + + present = sfp_is_port_present(client, data->port); + if (IS_ERR_VALUE(present)) { + return present; + } + + if (present == 0) { + /* port is not present */ + return -ENXIO; + } + + data = qsfp_update_tx_rx_status(dev); + if (IS_ERR(data)) { + return PTR_ERR(data); + } + + switch (attr->index) { + case TX_FAULT: + val = !!(data->qsfp->status[2] & 0xF); + break; + case TX_FAULT1: + case TX_FAULT2: + case TX_FAULT3: + case TX_FAULT4: + val = !!(data->qsfp->status[2] & BIT_INDEX(attr->index - TX_FAULT1)); + break; + case TX_DISABLE: + val = data->qsfp->status[1] & 0xF; + break; + case TX_DISABLE1: + case TX_DISABLE2: + case TX_DISABLE3: + case TX_DISABLE4: + val = !!(data->qsfp->status[1] & BIT_INDEX(attr->index - TX_DISABLE1)); + break; + case RX_LOS: + val = !!(data->qsfp->status[0] & 0xF); + break; + case RX_LOS1: + case RX_LOS2: + case RX_LOS3: + case RX_LOS4: + val = !!(data->qsfp->status[0] & BIT_INDEX(attr->index - RX_LOS1)); + break; + default: + break; + } + + return sprintf(buf, "%d\n", val); +} + +static ssize_t qsfp_set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + long disable; + int status; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + + status = sfp_is_port_present(client, data->port); + if (IS_ERR_VALUE(status)) { + return status; + } + + if (!status) { + /* port is not present */ + return -ENXIO; + } + + status = kstrtol(buf, 10, &disable); + if (status) { + return status; + } + + data = qsfp_update_tx_rx_status(dev); + if (IS_ERR(data)) { + return PTR_ERR(data); + } + + mutex_lock(&data->update_lock); + + if (attr->index == TX_DISABLE) { + if (disable) { + data->qsfp->status[1] |= 0xF; + } + else { + data->qsfp->status[1] &= ~0xF; + } + } + else {/* TX_DISABLE1 ~ TX_DISABLE4*/ + if (disable) { + data->qsfp->status[1] |= (1 << (attr->index - TX_DISABLE1)); + } + else { + data->qsfp->status[1] &= ~(1 << (attr->index - TX_DISABLE1)); + } + } + + DEBUG_PRINT("index = (%d), status = (0x%x)", attr->index, data->qsfp->status[1]); + status = sfp_eeprom_write(data->client, SFF8436_TX_DISABLE_ADDR, &data->qsfp->status[1], sizeof(data->qsfp->status[1])); + if (unlikely(status < 0)) { + count = status; + } + + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t sfp_eeprom_write(struct i2c_client *client, u8 command, const char *data, + int data_len) +{ +#if USE_I2C_BLOCK_READ + int status, retry = I2C_RW_RETRY_COUNT; + + if (data_len > I2C_SMBUS_BLOCK_MAX) { + data_len = I2C_SMBUS_BLOCK_MAX; + } + + while (retry) { + status = i2c_smbus_write_i2c_block_data(client, command, data_len, data); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(status < 0)) { + return status; + } + + return data_len; +#else + int status, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_write_byte_data(client, command, *data); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(status < 0)) { + return status; + } + + return 1; +#endif + + +} + +#if (MULTIPAGE_SUPPORT == 0) +static ssize_t sfp_port_write(struct sfp_port_data *data, + 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(&data->update_lock); + + while (count) { + ssize_t status; + + status = sfp_eeprom_write(data->client, off, buf, count); + if (status <= 0) { + if (retval == 0) { + retval = status; + } + break; + } + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&data->update_lock); + return retval; +} +#endif + +static ssize_t sfp_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + int present; + struct sfp_port_data *data; + DEBUG_PRINT("%s(%d) offset = (%d), count = (%d)", off, count); + data = dev_get_drvdata(container_of(kobj, struct device, kobj)); + + present = sfp_is_port_present(data->client, data->port); + if (IS_ERR_VALUE(present)) { + return present; + } + + if (present == 0) { + /* port is not present */ + return -ENODEV; + } + +#if (MULTIPAGE_SUPPORT == 1) + return sfp_port_read_write(data, buf, off, count, QSFP_WRITE_OP); +#else + return sfp_port_write(data, buf, off, count); +#endif +} + +static ssize_t sfp_eeprom_read(struct i2c_client *client, u8 command, u8 *data, + int data_len) +{ +#if USE_I2C_BLOCK_READ + int status, retry = I2C_RW_RETRY_COUNT; + + if (data_len > I2C_SMBUS_BLOCK_MAX) { + data_len = I2C_SMBUS_BLOCK_MAX; + } + + while (retry) { + status = i2c_smbus_read_i2c_block_data(client, command, data_len, data); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(status < 0)) { + goto abort; + } + if (unlikely(status != data_len)) { + status = -EIO; + goto abort; + } + + //result = data_len; + +abort: + return status; +#else + int status, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, command); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(status < 0)) { + dev_dbg(&client->dev, "sfp read byte data failed, command(0x%2x), data(0x%2x)\r\n", command, status); + goto abort; + } + + *data = (u8)status; + status = 1; + +abort: + return status; +#endif +} + +#if (MULTIPAGE_SUPPORT == 1) +/*-------------------------------------------------------------------------*/ +/* + * This routine computes the addressing information to be used for + * a given r/w request. + * + * Task is to calculate the client (0 = i2c addr 50, 1 = i2c addr 51), + * the page, and the offset. + * + * Handles both SFP and QSFP. + * For SFP, offset 0-255 are on client[0], >255 is on client[1] + * Offset 256-383 are on the lower half of client[1] + * Pages are accessible on the upper half of client[1]. + * Offset >383 are in 128 byte pages mapped into the upper half + * + * For QSFP, all offsets are on client[0] + * offset 0-127 are on the lower half of client[0] (no paging) + * Pages are accessible on the upper half of client[1]. + * Offset >127 are in 128 byte pages mapped into the upper half + * + * Callers must not read/write beyond the end of a client or a page + * without recomputing the client/page. Hence offset (within page) + * plus length must be less than or equal to 128. (Note that this + * routine does not have access to the length of the call, hence + * cannot do the validity check.) + * + * Offset within Lower Page 00h and Upper Page 00h are not recomputed + */ +static uint8_t sff_8436_translate_offset(struct sfp_port_data *port_data, + loff_t *offset, struct i2c_client **client) +{ + unsigned page = 0; + + *client = port_data->client; + + /* + * if offset is in the range 0-128... + * page doesn't matter (using lower half), return 0. + * offset is already correct (don't add 128 to get to paged area) + */ + if (*offset < SFF_8436_PAGE_SIZE) + return page; + + /* note, page will always be positive since *offset >= 128 */ + page = (*offset >> 7)-1; + /* 0x80 places the offset in the top half, offset is last 7 bits */ + *offset = SFF_8436_PAGE_SIZE + (*offset & 0x7f); + + return page; /* note also returning client and offset */ +} + +static ssize_t sff_8436_eeprom_read(struct sfp_port_data *port_data, + struct i2c_client *client, + char *buf, unsigned offset, size_t count) +{ + struct i2c_msg msg[2]; + u8 msgbuf[2]; + unsigned long timeout, read_time; + int status, i; + + memset(msg, 0, sizeof(msg)); + + switch (port_data->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* + * When we have a better choice than SMBus calls, use a + * combined I2C message. Write address; then read up to + * io_limit data bytes. msgbuf is u8 and will cast to our + * needs. + */ + i = 0; + msgbuf[i++] = offset; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + read_time = jiffies; + + switch (port_data->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_read_i2c_block_data(client, offset, + count, buf); + break; + case I2C_SMBUS_WORD_DATA: + status = i2c_smbus_read_word_data(client, offset); + if (status >= 0) { + buf[0] = status & 0xff; + if (count == 2) + buf[1] = status >> 8; + status = count; + } + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_read_byte_data(client, offset); + if (status >= 0) { + buf[0] = status; + status = count; + } + break; + default: + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + status = count; + } + + dev_dbg(&client->dev, "eeprom read %zu@%d --> %d (%ld)\n", + count, offset, status, jiffies); + + if (status == count) /* happy path */ + return count; + + if (status == -ENXIO) /* no module present */ + return status; + + /* REVISIT: at HZ=100, this is sloooow */ + msleep(1); + } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; +} + +static ssize_t sff_8436_eeprom_write(struct sfp_port_data *port_data, + struct i2c_client *client, + const char *buf, + unsigned offset, size_t count) +{ + struct i2c_msg msg; + ssize_t status; + unsigned long timeout, write_time; + unsigned next_page_start; + int i = 0; + + /* write max is at most a page + * (In this driver, write_max is actually one byte!) + */ + if (count > port_data->write_max) + count = port_data->write_max; + + /* shorten count if necessary to avoid crossing page boundary */ + next_page_start = roundup(offset + 1, SFF_8436_PAGE_SIZE); + if (offset + count > next_page_start) + count = next_page_start - offset; + + switch (port_data->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* If we'll use I2C calls for I/O, set up the message */ + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = port_data->writebuf; + + msg.buf[i++] = offset; + memcpy(&msg.buf[i], buf, count); + msg.len = i + count; + break; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + write_time = jiffies; + + switch (port_data->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_write_i2c_block_data(client, + offset, count, buf); + if (status == 0) + status = count; + break; + case I2C_SMBUS_WORD_DATA: + if (count == 2) { + status = i2c_smbus_write_word_data(client, + offset, (u16)((buf[0])|(buf[1] << 8))); + } else { + /* count = 1 */ + status = i2c_smbus_write_byte_data(client, + offset, buf[0]); + } + if (status == 0) + status = count; + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_write_byte_data(client, offset, + buf[0]); + if (status == 0) + status = count; + break; + default: + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + break; + } + + dev_dbg(&client->dev, "eeprom write %zu@%d --> %ld (%lu)\n", + count, offset, (long int) status, jiffies); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + msleep(1); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + + +static ssize_t sff_8436_eeprom_update_client(struct sfp_port_data *port_data, + char *buf, loff_t off, + size_t count, qsfp_opcode_e opcode) +{ + struct i2c_client *client; + ssize_t retval = 0; + u8 page = 0; + loff_t phy_offset = off; + int ret = 0; + + page = sff_8436_translate_offset(port_data, &phy_offset, &client); + + dev_dbg(&client->dev, + "sff_8436_eeprom_update_client off %lld page:%d phy_offset:%lld, count:%ld, opcode:%d\n", + off, page, phy_offset, (long int) count, opcode); + if (page > 0) { + ret = sff_8436_eeprom_write(port_data, client, &page, + SFF_8436_PAGE_SELECT_REG, 1); + if (ret < 0) { + dev_dbg(&client->dev, + "Write page register for page %d failed ret:%d!\n", + page, ret); + return ret; + } + } + + while (count) { + ssize_t status; + + if (opcode == QSFP_READ_OP) { + status = sff_8436_eeprom_read(port_data, client, + buf, phy_offset, count); + } else { + status = sff_8436_eeprom_write(port_data, client, + buf, phy_offset, count); + } + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + buf += status; + phy_offset += status; + count -= status; + retval += status; + } + + + if (page > 0) { + /* return the page register to page 0 (why?) */ + page = 0; + ret = sff_8436_eeprom_write(port_data, client, &page, + SFF_8436_PAGE_SELECT_REG, 1); + if (ret < 0) { + dev_err(&client->dev, + "Restore page register to page %d failed ret:%d!\n", + page, ret); + return ret; + } + } + return retval; +} + + +/* + * Figure out if this access is within the range of supported pages. + * Note this is called on every access because we don't know if the + * module has been replaced since the last call. + * If/when modules support more pages, this is the routine to update + * to validate and allow access to additional pages. + * + * Returns updated len for this access: + * - entire access is legal, original len is returned. + * - access begins legal but is too long, len is truncated to fit. + * - initial offset exceeds supported pages, return -EINVAL + */ +static ssize_t sff_8436_page_legal(struct sfp_port_data *port_data, + loff_t off, size_t len) +{ + struct i2c_client *client = port_data->client; + u8 regval; + int status; + size_t maxlen; + + if (off < 0) return -EINVAL; + if (port_data->driver_type == DRIVER_TYPE_SFP_MSA) { + /* SFP case */ + /* if no pages needed, we're good */ + if ((off + len) <= SFF_8472_EEPROM_UNPAGED_SIZE) return len; + /* if offset exceeds possible pages, we're not good */ + if (off >= SFF_8472_EEPROM_SIZE) return -EINVAL; + /* in between, are pages supported? */ + status = sff_8436_eeprom_read(port_data, client, ®val, + SFF_8472_PAGEABLE_REG, 1); + if (status < 0) return status; /* error out (no module?) */ + if (regval & SFF_8472_PAGEABLE) { + /* Pages supported, trim len to the end of pages */ + maxlen = SFF_8472_EEPROM_SIZE - off; + } else { + /* pages not supported, trim len to unpaged size */ + maxlen = SFF_8472_EEPROM_UNPAGED_SIZE - off; + } + len = (len > maxlen) ? maxlen : len; + dev_dbg(&client->dev, + "page_legal, SFP, off %lld len %ld\n", + off, (long int) len); + } + else if (port_data->driver_type == DRIVER_TYPE_QSFP || + port_data->driver_type == DRIVER_TYPE_XFP) { + /* QSFP case */ + /* if no pages needed, we're good */ + if ((off + len) <= SFF_8436_EEPROM_UNPAGED_SIZE) return len; + /* if offset exceeds possible pages, we're not good */ + if (off >= SFF_8436_EEPROM_SIZE) return -EINVAL; + /* in between, are pages supported? */ + status = sff_8436_eeprom_read(port_data, client, ®val, + SFF_8436_PAGEABLE_REG, 1); + if (status < 0) return status; /* error out (no module?) */ + if (regval & SFF_8436_NOT_PAGEABLE) { + /* pages not supported, trim len to unpaged size */ + maxlen = SFF_8436_EEPROM_UNPAGED_SIZE - off; + } else { + /* Pages supported, trim len to the end of pages */ + maxlen = SFF_8436_EEPROM_SIZE - off; + } + len = (len > maxlen) ? maxlen : len; + dev_dbg(&client->dev, + "page_legal, QSFP, off %lld len %ld\n", + off, (long int) len); + } + else { + return -EINVAL; + } + return len; +} + + +static ssize_t sfp_port_read_write(struct sfp_port_data *port_data, + char *buf, loff_t off, size_t len, qsfp_opcode_e opcode) +{ + struct i2c_client *client = port_data->client; + int chunk; + int status = 0; + ssize_t retval; + size_t pending_len = 0, chunk_len = 0; + loff_t chunk_offset = 0, chunk_start_offset = 0; + + if (unlikely(!len)) + return len; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&port_data->update_lock); + + /* + * Confirm this access fits within the device suppored addr range + */ + len = sff_8436_page_legal(port_data, off, len); + if (len < 0) { + status = len; + goto err; + } + + /* + * For each (128 byte) chunk involved in this request, issue a + * separate call to sff_eeprom_update_client(), to + * ensure that each access recalculates the client/page + * and writes the page register as needed. + * Note that chunk to page mapping is confusing, is different for + * QSFP and SFP, and never needs to be done. Don't try! + */ + pending_len = len; /* amount remaining to transfer */ + retval = 0; /* amount transferred */ + for (chunk = off >> 7; chunk <= (off + len - 1) >> 7; chunk++) { + + /* + * Compute the offset and number of bytes to be read/write + * + * 1. start at offset 0 (within the chunk), and read/write + * the entire chunk + * 2. start at offset 0 (within the chunk) and read/write less + * than entire chunk + * 3. start at an offset not equal to 0 and read/write the rest + * of the chunk + * 4. start at an offset not equal to 0 and read/write less than + * (end of chunk - offset) + */ + chunk_start_offset = chunk * SFF_8436_PAGE_SIZE; + + if (chunk_start_offset < off) { + chunk_offset = off; + if ((off + pending_len) < (chunk_start_offset + + SFF_8436_PAGE_SIZE)) + chunk_len = pending_len; + else + chunk_len = (chunk+1)*SFF_8436_PAGE_SIZE - off;/*SFF_8436_PAGE_SIZE - off;*/ + } else { + chunk_offset = chunk_start_offset; + if (pending_len > SFF_8436_PAGE_SIZE) + chunk_len = SFF_8436_PAGE_SIZE; + else + chunk_len = pending_len; + } + + dev_dbg(&client->dev, + "sff_r/w: off %lld, len %ld, chunk_start_offset %lld, chunk_offset %lld, chunk_len %ld, pending_len %ld\n", + off, (long int) len, chunk_start_offset, chunk_offset, + (long int) chunk_len, (long int) pending_len); + + /* + * note: chunk_offset is from the start of the EEPROM, + * not the start of the chunk + */ + status = sff_8436_eeprom_update_client(port_data, buf, + chunk_offset, chunk_len, opcode); + if (status != chunk_len) { + /* This is another 'no device present' path */ + dev_dbg(&client->dev, + "sff_8436_update_client for chunk %d chunk_offset %lld chunk_len %ld failed %d!\n", + chunk, chunk_offset, (long int) chunk_len, status); + goto err; + } + buf += status; + pending_len -= status; + retval += status; + } + mutex_unlock(&port_data->update_lock); + + return retval; + +err: + mutex_unlock(&port_data->update_lock); + + return status; +} + +#else +static ssize_t sfp_port_read(struct sfp_port_data *data, + char *buf, loff_t off, size_t count) +{ + ssize_t retval = 0; + + if (unlikely(!count)) { + DEBUG_PRINT("Count = 0, return"); + return count; + } + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&data->update_lock); + + while (count) { + ssize_t status; + + status = sfp_eeprom_read(data->client, off, buf, count); + if (status <= 0) { + if (retval == 0) { + retval = status; + } + break; + } + + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&data->update_lock); + return retval; + +} +#endif + +static ssize_t sfp_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + int present; + struct sfp_port_data *data; + DEBUG_PRINT("offset = (%d), count = (%d)", off, count); + + data = dev_get_drvdata(container_of(kobj, struct device, kobj)); + present = sfp_is_port_present(data->client, data->port); + if (IS_ERR_VALUE(present)) { + return present; + } + + if (present == 0) { + /* port is not present */ + return -ENODEV; + } + +#if (MULTIPAGE_SUPPORT == 1) + return sfp_port_read_write(data, buf, off, count, QSFP_READ_OP); +#else + return sfp_port_read(data, buf, off, count); +#endif +} + +#if (MULTIPAGE_SUPPORT == 1) +static int sfp_sysfs_eeprom_init(struct kobject *kobj, struct bin_attribute *eeprom, size_t size) +#else +static int sfp_sysfs_eeprom_init(struct kobject *kobj, struct bin_attribute *eeprom) +#endif +{ + int err; + + sysfs_bin_attr_init(eeprom); + eeprom->attr.name = EEPROM_NAME; + eeprom->attr.mode = S_IWUSR | S_IRUGO; + eeprom->read = sfp_bin_read; + eeprom->write = sfp_bin_write; +#if (MULTIPAGE_SUPPORT == 1) + eeprom->size = size; +#else + eeprom->size = EEPROM_SIZE; +#endif + + /* Create eeprom file */ + err = sysfs_create_bin_file(kobj, eeprom); + if (err) { + return err; + } + + return 0; +} + +static int sfp_sysfs_eeprom_cleanup(struct kobject *kobj, struct bin_attribute *eeprom) +{ + sysfs_remove_bin_file(kobj, eeprom); + return 0; +} + + +#if (MULTIPAGE_SUPPORT == 0) +static int sfp_i2c_check_functionality(struct i2c_client *client) +{ +#if USE_I2C_BLOCK_READ + return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK); +#else + return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA); +#endif +} +#endif + + +static const struct attribute_group qsfp_group = { + .attrs = qsfp_attributes, +}; + +static int qsfp_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, + struct qsfp_data **data) +{ + int status; + struct qsfp_data *qsfp; + +#if (MULTIPAGE_SUPPORT == 0) + if (!sfp_i2c_check_functionality(client)) { + status = -EIO; + goto exit; + } +#endif + + qsfp = kzalloc(sizeof(struct qsfp_data), GFP_KERNEL); + if (!qsfp) { + status = -ENOMEM; + goto exit; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &qsfp_group); + if (status) { + goto exit_free; + } + + /* init eeprom */ +#if (MULTIPAGE_SUPPORT == 1) + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &qsfp->eeprom.bin, SFF_8436_EEPROM_SIZE); +#else + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &qsfp->eeprom.bin); +#endif + if (status) { + goto exit_remove; + } + + *data = qsfp; + dev_info(&client->dev, "qsfp '%s'\n", client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &qsfp_group); +exit_free: + kfree(qsfp); +exit: + + return status; +} + +/* Platform dependent +++ */ +static int sfp_device_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int ret = 0; + struct sfp_port_data *data = NULL; + + if (client->addr != SFP_EEPROM_A0_I2C_ADDR) { + return -ENODEV; + } + + if (dev_id->driver_data < as7816_64x_port1 || dev_id->driver_data > as7816_64x_port64) { + return -ENXIO; + } + + data = kzalloc(sizeof(struct sfp_port_data), GFP_KERNEL); + if (!data) { + return -ENOMEM; + } + +#if (MULTIPAGE_SUPPORT == 1) + data->use_smbus = 0; + + /* Use I2C operations unless we're stuck with SMBus extensions. */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + data->use_smbus = I2C_SMBUS_I2C_BLOCK_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + data->use_smbus = I2C_SMBUS_WORD_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + data->use_smbus = I2C_SMBUS_BYTE_DATA; + } else { + ret = -EPFNOSUPPORT; + goto exit_kfree; + } + } + + if (!data->use_smbus || + (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_WORD_DATA) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + /* + * NOTE: AN-2079 + * Finisar recommends that the host implement 1 byte writes + * only since this module only supports 32 byte page boundaries. + * 2 byte writes are acceptable for PE and Vout changes per + * Application Note AN-2071. + */ + unsigned write_max = 1; + + if (write_max > io_limit) + write_max = io_limit; + if (data->use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) + write_max = I2C_SMBUS_BLOCK_MAX; + data->write_max = write_max; + + /* buffer (data + address at the beginning) */ + data->writebuf = kmalloc(write_max + 2, GFP_KERNEL); + if (!data->writebuf) { + ret = -ENOMEM; + goto exit_kfree; + } + } else { + dev_warn(&client->dev, + "cannot write due to controller restrictions."); + } + + if (data->use_smbus == I2C_SMBUS_WORD_DATA || + data->use_smbus == I2C_SMBUS_BYTE_DATA) { + dev_notice(&client->dev, "Falling back to %s reads, " + "performance will suffer\n", data->use_smbus == + I2C_SMBUS_WORD_DATA ? "word" : "byte"); + } +#endif + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->port = dev_id->driver_data; + data->client = client; + data->driver_type = DRIVER_TYPE_QSFP; + + ret = qsfp_probe(client, dev_id, &data->qsfp); + if (ret < 0) { + goto exit_kfree_buf; + } + + return ret; + +exit_kfree_buf: +#if (MULTIPAGE_SUPPORT == 1) + if (data->writebuf) kfree(data->writebuf); +#endif + +exit_kfree: + kfree(data); + return ret; +} +/* Platform dependent --- */ + +static int qsfp_remove(struct i2c_client *client, struct qsfp_data *data) +{ + sfp_sysfs_eeprom_cleanup(&client->dev.kobj, &data->eeprom.bin); + sysfs_remove_group(&client->dev.kobj, &qsfp_group); + kfree(data); + return 0; +} + +static int sfp_device_remove(struct i2c_client *client) +{ + int ret = 0; + struct sfp_port_data *data = i2c_get_clientdata(client); + + if (data->driver_type == DRIVER_TYPE_QSFP) { + ret = qsfp_remove(client, data->qsfp); + } + + kfree(data); + return ret; +} + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +static struct i2c_driver sfp_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = sfp_device_probe, + .remove = sfp_device_remove, + .id_table = sfp_device_id, + .address_list = normal_i2c, +}; + +static int __init sfp_init(void) +{ + return i2c_add_driver(&sfp_driver); +} + +static void __exit sfp_exit(void) +{ + i2c_del_driver(&sfp_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton as7816_64x_sfp driver"); +MODULE_LICENSE("GPL"); + +module_init(sfp_init); +module_exit(sfp_exit); + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/ym2651y.c b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/ym2651y.c new file mode 120000 index 000000000000..f4d67640ccc3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/ym2651y.c @@ -0,0 +1 @@ +../../common/modules/ym2651y.c \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/service/as7816-platform-init.service b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/service/as7816-platform-init.service new file mode 100755 index 000000000000..c1ba30092f1d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/service/as7816-platform-init.service @@ -0,0 +1,13 @@ +[Unit] +Description=Accton AS7816-64X Platform initialization service +Before=pmon.service +DefaultDependencies=no + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/accton_as7816_util.py install +ExecStop=/usr/local/bin/accton_as7816_util.py clean +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/setup.py b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/setup.py new file mode 100755 index 000000000000..6633b9438b86 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/setup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +import os +import sys +from setuptools import setup +os.listdir + +setup( + name='as7816_64x', + version='1.0', + description='Module to initialize Accton AS7816-64X platforms', + + packages=['as7816_64x'], + package_dir={'as7816_64x': 'as7816-64x/classes'}, +) + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/utils/README b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/utils/README new file mode 100755 index 000000000000..0b9fc1633999 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/utils/README @@ -0,0 +1,60 @@ +Copyright (C) 2016 Accton Networks, Inc. + +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 3 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, see . + +To initialize the system, run "accton_as7712_util.py install". +To clean up the drivers & devices, run "accton_as7712_util.py clean". +To dump information of sensors, run "accton_as7712_util.py show". +To dump SFP EEPROM, run "accton_as7712_util.py sff". +To set fan speed, run "accton_as7712_util.py set fan". +To enable/disable SFP emission, run "accton_as7712_util.py set sfp". +To set system LEDs' color, run "accton_as7712_util.py set led" +For more information, run "accton_as7712_util.py --help". + +==================================================================== +Besides applying accton_as7712_util.py to access peripherals, you can +access peripherals by sysfs nodes directly after the installation is run. + +LED controls can be found under /sys/class/leds. The sysfs interface +color mappings are as follows: +Brightness: + 0 => off + 1 => green + 2 => amber + 3 => red + 4 => blue + +There are 5 system LEDs, loc, diag, fan, ps1, and ps2. +They are lit automatically by CPLD, but the loc and diag. +The loc led has only 1 color, blue. +The diag one has 3 colors: red, amber, and green. + +Fan controls can be found in /sys/bus/i2c/devices/2-0066. +There are 12 fans inside 6 fan modules. +All fans share 1 duty setting, ranged from 0~100. + +Three temperature sensors are controlled by the lm75 kernel modules. +They should already be visible under /sys/bus/i2c/drivers/lm75/. + +Two power supplies are controlled by the CPLD. +Here provide their status under +/sys/bus/i2c/devices/10-0050 and /sys/bus/i2c/devices/11-0053. + +There are 32 QSFP+ modules are equipped. +Apply "accton_as7712_util.py show" to get their status. +Apply "accton_as7712_util.py set sfp" to turn on/off light transmission. +Apply "accton_as7712_util.py sff" to dump EEPROM information. +Before operating on that QSFP+, please make sure it is well plugged. +Otherwise, operation is going to fail. + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/utils/accton_as7816_util.py b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/utils/accton_as7816_util.py new file mode 100755 index 000000000000..44c4486a2d59 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/utils/accton_as7816_util.py @@ -0,0 +1,523 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 Accton Networks, Inc. +# +# 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 3 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, see . + +""" +Usage: %(scriptName)s [options] command object + +options: + -h | --help : this help message + -d | --debug : run with debug mode + -f | --force : ignore error during installation or clean +command: + install : install drivers and generate related sysfs nodes + clean : uninstall drivers and remove related sysfs nodes + show : show all systen status + sff : dump SFP eeprom + set : change board setting with fan|led|sfp +""" + +import os +import commands +import sys, getopt +import logging +import re +import time +from collections import namedtuple + + + + +PROJECT_NAME = 'as7816_64x' +version = '0.1.0' +verbose = False +DEBUG = False +args = [] +ALL_DEVICE = {} +DEVICE_NO = {'led':5, 'fan':4,'thermal':6, 'psu':2, 'sfp':64} +FORCE = 0 +#logging.basicConfig(filename= PROJECT_NAME+'.log', filemode='w',level=logging.DEBUG) +#logging.basicConfig(level=logging.INFO) + + +if DEBUG == True: + print sys.argv[0] + print 'ARGV :', sys.argv[1:] + + +def main(): + global DEBUG + global args + global FORCE + + if len(sys.argv)<2: + show_help() + + options, args = getopt.getopt(sys.argv[1:], 'hdf', ['help', + 'debug', + 'force', + ]) + if DEBUG == True: + print options + print args + print len(sys.argv) + + for opt, arg in options: + if opt in ('-h', '--help'): + show_help() + elif opt in ('-d', '--debug'): + DEBUG = True + logging.basicConfig(level=logging.INFO) + elif opt in ('-f', '--force'): + FORCE = 1 + else: + logging.info('no option') + for arg in args: + if arg == 'install': + do_install() + elif arg == 'clean': + do_uninstall() + elif arg == 'show': + device_traversal() + elif arg == 'sff': + if len(args)!=2: + show_eeprom_help() + elif int(args[1]) ==0 or int(args[1]) > DEVICE_NO['sfp']: + show_eeprom_help() + else: + show_eeprom(args[1]) + return + elif arg == 'set': + if len(args)<3: + show_set_help() + else: + set_device(args[1:]) + return + else: + show_help() + + + return 0 + +def show_help(): + print __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]} + sys.exit(0) + +def show_set_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print cmd +" [led|sfp|fan]" + print " use \""+ cmd + " led 0-4 \" to set led color" + print " use \""+ cmd + " fan 0-100\" to set fan duty percetage" + print " use \""+ cmd + " sfp 1-32 {0|1}\" to set sfp# tx_disable" + sys.exit(0) + +def show_eeprom_help(): + cmd = sys.argv[0].split("/")[-1]+ " " + args[0] + print " use \""+ cmd + " 1-32 \" to dump sfp# eeprom" + sys.exit(0) + +def my_log(txt): + if DEBUG == True: + print "[ROY]"+txt + return + +def log_os_system(cmd, show): + logging.info('Run :'+cmd) + status, output = commands.getstatusoutput(cmd) + my_log (cmd +"with result:" + str(status)) + my_log (" output:"+output) + if status: + logging.info('Failed :'+cmd) + if show: + print('Failed :'+cmd) + return status, output + +def driver_check(): + ret, lsmod = log_os_system("lsmod| grep accton", 0) + logging.info('mods:'+lsmod) + if len(lsmod) ==0: + return False + return True + + + +kos = [ +'modprobe i2c_dev', +'modprobe i2c_mux_pca954x', +'modprobe accton_i2c_cpld' , +'modprobe ym2651y' , +'modprobe x86-64-accton-as7816-64x-fan' , +'modprobe x86-64-accton-as7816-64x-sfp' , +'modprobe x86-64-accton-as7816-64x-leds' , +'modprobe x86-64-accton-as7816-64x-psu' ] + +def driver_install(): + global FORCE + status, output = log_os_system("depmod", 1) + for i in range(0,len(kos)): + status, output = log_os_system(kos[i], 1) + if status: + if FORCE == 0: + return status + return 0 + +def driver_uninstall(): + global FORCE + for i in range(0,len(kos)): + rm = kos[-(i+1)].replace("modprobe", "modprobe -rq") + rm = rm.replace("insmod", "rmmod") + status, output = log_os_system(rm, 1) + if status: + if FORCE == 0: + return status + return 0 + +led_prefix ='/sys/class/leds/'+PROJECT_NAME+'_led::' +hwmon_types = {'led': ['diag','fan','loc','psu1','psu2']} +hwmon_nodes = {'led': ['brightness'] } +hwmon_prefix ={'led': led_prefix} + +i2c_prefix = '/sys/bus/i2c/devices/' +i2c_bus = {'fan': ['17-0068'] , + 'thermal': ['18-0048','18-0049', '18-004a' , '18-004b', '17-004d', '17-004e'] , + 'psu': ['10-0053','9-0050'], + 'sfp': ['-0050']} +i2c_nodes = {'fan': ['present', 'front_speed_rpm', 'rear_speed_rpm'] , + 'thermal': ['hwmon/hwmon*/temp1_input'] , + 'psu': ['psu_present ', 'psu_power_good'] , + 'sfp': ['sfp_is_present ', 'sfp_tx_disable']} + +sfp_map = [37,38,39,40,42,41,44,43,33,34,35,36,45,46,47,48,49,50,51,52, + 61,62,63,64,53,54,55,56,57,58,59,60,69,70,71,72,77,78,79,80,65, + 66,67,68,73,74,75,76,85,86,87,88,31,32,29,30,81,82,83,84,25,26, + 27,28] + +mknod =[ +'echo pca9548 0x77 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x76 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x73 > /sys/bus/i2c/devices/i2c-1/new_device', +'echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-2/new_device', +'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-2/new_device', +'echo pca9548 0x72 > /sys/bus/i2c/devices/i2c-2/new_device', +'echo pca9548 0x73 > /sys/bus/i2c/devices/i2c-2/new_device', +'echo pca9548 0x74 > /sys/bus/i2c/devices/i2c-2/new_device', +'echo pca9548 0x75 > /sys/bus/i2c/devices/i2c-2/new_device', +'echo pca9548 0x76 > /sys/bus/i2c/devices/i2c-2/new_device', +'echo 24c02 0x56 > /sys/bus/i2c/devices/i2c-0/new_device', +'echo as7816_64x_psu1 0x53 > /sys/bus/i2c/devices/i2c-10/new_device', +'echo ym2851 0x5b > /sys/bus/i2c/devices/i2c-10/new_device', +'echo as7816_64x_psu2 0x50 > /sys/bus/i2c/devices/i2c-9/new_device', +'echo ym2851 0x58 > /sys/bus/i2c/devices/i2c-9/new_device', +'echo as7816_64x_fan 0x68 > /sys/bus/i2c/devices/i2c-17/new_device', +'echo lm75 0x48 > /sys/bus/i2c/devices/i2c-18/new_device', +'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-18/new_device', +'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-18/new_device', +'echo lm75 0x4b > /sys/bus/i2c/devices/i2c-18/new_device', +'echo lm75 0x4d > /sys/bus/i2c/devices/i2c-17/new_device', +'echo lm75 0x4e > /sys/bus/i2c/devices/i2c-17/new_device', +'echo cpld_as7816 0x60 > /sys/bus/i2c/devices/i2c-19/new_device', +'echo cpld_plain 0x62 > /sys/bus/i2c/devices/i2c-20/new_device', +'echo cpld_plain 0x64 > /sys/bus/i2c/devices/i2c-21/new_device', +'echo cpld_plain 0x66 > /sys/bus/i2c/devices/i2c-22/new_device'] + +def i2c_order_check(): + return 0 + +def device_install(): + global FORCE + + for i in range(0,len(mknod)): + #for pca954x need times to built new i2c buses + if mknod[i].find('pca954') != -1: + time.sleep(1) + + status, output = log_os_system(mknod[i], 1) + if status: + print output + if FORCE == 0: + return status + + for i in range(0,len(sfp_map)): + status, output =log_os_system("echo as7816_64x_port"+str(i+1)+" 0x50 > /sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/new_device", 1) + if status: + print output + if FORCE == 0: + return status + return + +def device_uninstall(): + global FORCE + + status, output =log_os_system("ls /sys/bus/i2c/devices/1-0076", 0) + + for i in range(0,len(sfp_map)): + target = "/sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/delete_device" + status, output =log_os_system("echo 0x50 > "+ target, 1) + if status: + print output + if FORCE == 0: + return status + + nodelist = mknod + + for i in range(len(nodelist)): + target = nodelist[-(i+1)] + temp = target.split() + del temp[1] + temp[-1] = temp[-1].replace('new_device', 'delete_device') + status, output = log_os_system(" ".join(temp), 1) + if status: + print output + if FORCE == 0: + return status + + return + +def system_ready(): + if driver_check() == False: + return False + if not device_exist(): + return False + return True + +def do_install(): + print "Checking system...." + if driver_check() == False: + print "No driver, installing...." + status = driver_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" drivers detected...." + if not device_exist(): + print "No device, installing...." + status = device_install() + if status: + if FORCE == 0: + return status + else: + print PROJECT_NAME.upper()+" devices detected...." + return + +def do_uninstall(): + print "Checking system...." + if not device_exist(): + print PROJECT_NAME.upper() +" has no device installed...." + else: + print "Removing device...." + status = device_uninstall() + if status: + if FORCE == 0: + return status + + if driver_check()== False : + print PROJECT_NAME.upper() +" has no driver installed...." + else: + print "Removing installed driver...." + status = driver_uninstall() + if status: + if FORCE == 0: + return status + + return + +def devices_info(): + global DEVICE_NO + global ALL_DEVICE + global i2c_bus, hwmon_types + for key in DEVICE_NO: + ALL_DEVICE[key]= {} + for i in range(0,DEVICE_NO[key]): + ALL_DEVICE[key][key+str(i+1)] = [] + + for key in i2c_bus: + buses = i2c_bus[key] + nodes = i2c_nodes[key] + for i in range(0,len(buses)): + for j in range(0,len(nodes)): + if 'fan' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ buses[i]+"/fan"+str(k+1)+"_"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + elif 'sfp' == key: + for k in range(0,DEVICE_NO[key]): + node = key+str(k+1) + path = i2c_prefix+ str(sfp_map[k])+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + else: + node = key+str(i+1) + path = i2c_prefix+ buses[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][node].append(path) + + for key in hwmon_types: + itypes = hwmon_types[key] + nodes = hwmon_nodes[key] + for i in range(0,len(itypes)): + for j in range(0,len(nodes)): + node = key+"_"+itypes[i] + path = hwmon_prefix[key]+ itypes[i]+"/"+ nodes[j] + my_log(node+": "+ path) + ALL_DEVICE[key][ key+str(i+1)].append(path) + + #show dict all in the order + if DEBUG == True: + for i in sorted(ALL_DEVICE.keys()): + print(i+": ") + for j in sorted(ALL_DEVICE[i].keys()): + print(" "+j) + for k in (ALL_DEVICE[i][j]): + print(" "+" "+k) + return + +def show_eeprom(index): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + node = ALL_DEVICE['sfp'] ['sfp'+str(index)][0] + node = node.replace(node.split("/")[-1], 'sfp_eeprom') + # check if got hexdump command in current environment + ret, log = log_os_system("which hexdump", 0) + ret, log2 = log_os_system("which busybox hexdump", 0) + if len(log): + hex_cmd = 'hexdump' + elif len(log2): + hex_cmd = ' busybox hexdump' + else: + log = 'Failed : no hexdump cmd!!' + logging.info(log) + print log + return 1 + + print node + ":" + ret, log = log_os_system("cat "+node+"| "+hex_cmd+" -C", 1) + if ret==0: + print log + else: + print "**********device no found**********" + return + +def set_device(args): + global DEVICE_NO + global ALL_DEVICE + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + + if args[0]=='led': + if int(args[1])>4: + show_set_help() + return + #print ALL_DEVICE['led'] + for i in range(0,len(ALL_DEVICE['led'])): + for k in (ALL_DEVICE['led']['led'+str(i+1)]): + ret, log = log_os_system("echo "+args[1]+" >"+k, 1) + if ret: + return ret + elif args[0]=='fan': + if int(args[1])>100: + show_set_help() + return + #print ALL_DEVICE['fan'] + #fan1~6 is all fine, all fan share same setting + node = ALL_DEVICE['fan'] ['fan1'][0] + node = node.replace(node.split("/")[-1], 'fan_duty_cycle_percentage') + ret, log = log_os_system("cat "+ node, 1) + if ret==0: + print ("Previous fan duty: " + log.strip() +"%") + ret, log = log_os_system("echo "+args[1]+" >"+node, 1) + if ret==0: + print ("Current fan duty: " + args[1] +"%") + return ret + elif args[0]=='sfp': + if int(args[1])> DEVICE_NO[args[0]] or int(args[1])==0: + show_set_help() + return + if len(args)<2: + show_set_help() + return + + if int(args[2])>1: + show_set_help() + return + + #print ALL_DEVICE[args[0]] + for i in range(0,len(ALL_DEVICE[args[0]])): + for j in ALL_DEVICE[args[0]][args[0]+str(args[1])]: + if j.find('tx_disable')!= -1: + ret, log = log_os_system("echo "+args[2]+" >"+ j, 1) + if ret: + return ret + + return + +#get digits inside a string. +#Ex: 31 for "sfp31" +def get_value(input): + digit = re.findall('\d+', input) + return int(digit[0]) + +def device_traversal(): + if system_ready()==False: + print("System's not ready.") + print("Please install first!") + return + + if len(ALL_DEVICE)==0: + devices_info() + for i in sorted(ALL_DEVICE.keys()): + print("============================================") + print(i.upper()+": ") + print("============================================") + + for j in sorted(ALL_DEVICE[i].keys(), key=get_value): + print " "+j+":", + for k in (ALL_DEVICE[i][j]): + ret, log = log_os_system("cat "+k, 0) + func = k.split("/")[-1].strip() + func = re.sub(j+'_','',func,1) + func = re.sub(i.lower()+'_','',func,1) + if ret==0: + print func+"="+log+" ", + else: + print func+"="+"X"+" ", + print + print("----------------------------------------------------------------") + + + print + return + +def device_exist(): + ret1, log = log_os_system("ls "+i2c_prefix+"*0076", 0) + ret2, log = log_os_system("ls "+i2c_prefix+"i2c-2", 0) + return not(ret1 or ret2) + +if __name__ == "__main__": + main() diff --git a/platform/broadcom/sonic-platform-modules-accton/common/modules/accton_i2c_cpld.c b/platform/broadcom/sonic-platform-modules-accton/common/modules/accton_i2c_cpld.c new file mode 100644 index 000000000000..aae4e5538b63 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/common/modules/accton_i2c_cpld.c @@ -0,0 +1,887 @@ +/* + * A hwmon driver for the accton_i2c_cpld + * + * Copyright (C) 2013 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define MAX_PORT_NUM 64 +#define I2C_RW_RETRY_COUNT 10 +#define I2C_RW_RETRY_INTERVAL 60 /* ms */ + +#define I2C_ADDR_CPLD1 0x60 +#define I2C_ADDR_CPLD2 0x62 +#define I2C_ADDR_CPLD3 0x64 +#define CPLD_ADDRS {I2C_ADDR_CPLD1, I2C_ADDR_CPLD2, I2C_ADDR_CPLD3} + + +/* + * Number of additional attribute pointers to allocate + * with each call to krealloc + */ +#define ATTR_ALLOC_SIZE 1 /*For last attribute which is NUll.*/ + +#define NAME_SIZE 24 +#define MAX_RESP_LENGTH 48 + +typedef ssize_t (*show_func)( struct device *dev, + struct device_attribute *attr, + char *buf); +typedef ssize_t (*store_func)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +enum models { + AS7712_32X, + AS7716_32X, + AS7816_64X, + AS7312_54X, + PLAIN_CPLD, /*No attribute but add i2c addr to the list.*/ + NUM_MODEL +}; + +enum sfp_func { + HAS_SFP = 1<<0 , + HAS_QSFP = 1<<1 , +}; + +enum common_attrs { + CMN_VERSION, + CMN_ACCESS, + CMN_PRESENT_ALL, + NUM_COMMON_ATTR +}; + +enum sfp_attrs { + SFP_PRESENT, + SFP_RESET, + SFP_LP_MODE, + NUM_SFP_ATTR +}; + +struct cpld_sensor { + struct cpld_sensor *next; + char name[NAME_SIZE+1]; /* sysfs sensor name */ + struct device_attribute attribute; + bool update; /* runtime sensor update needed */ + int data; /* Sensor data. Negative if there was a read error */ + + u8 reg; /* register */ + u8 mask; /* bit mask */ + bool invert; /* inverted value*/ + +}; + +#define to_cpld_sensor(_attr) \ + container_of(_attr, struct cpld_sensor, attribute) + +struct cpld_data { + struct device *dev; + struct device *hwmon_dev; + + int num_attributes; + struct attribute_group group; + + enum models model; + struct cpld_sensor *sensors; + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + int attr_index; + u16 sfp_num; + u8 sfp_types; + struct model_attrs *cmn_attr; +}; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + + +struct base_attrs { + const char *name; + umode_t mode; + show_func get; + store_func set; +}; + +struct attrs { + int reg; + bool invert; + struct base_attrs *base; +}; + +struct model_attrs { + struct attrs **cmn; + struct attrs **portly; +}; + + +static ssize_t show_bit(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t show_presnet_all(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t set_1bit(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t set_byte(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); + +int accton_i2c_cpld_read(u8 cpld_addr, u8 reg); +int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + + +struct base_attrs common_attrs[NUM_COMMON_ATTR] = +{ + [CMN_VERSION] = {"version", S_IRUGO, show_bit, NULL}, + [CMN_ACCESS] = {"access", S_IWUSR, NULL, set_byte}, + [CMN_PRESENT_ALL] = {"module_present_all", S_IRUGO, show_presnet_all, NULL}, +}; + +struct attrs as7712_common[] = { + [CMN_VERSION] = {0x01, false, &common_attrs[CMN_VERSION]}, + [CMN_ACCESS] = {0x00, false, &common_attrs[CMN_ACCESS]}, + [CMN_PRESENT_ALL] = {0x30, false, &common_attrs[CMN_PRESENT_ALL]}, +}; +struct attrs as7816_common[] = { + [CMN_VERSION] = {0x01, false, &common_attrs[CMN_VERSION]}, + [CMN_ACCESS] = {0x00, false, &common_attrs[CMN_ACCESS]}, + [CMN_PRESENT_ALL] = {0x30, false, &common_attrs[CMN_PRESENT_ALL]}, +}; +struct attrs as7312_common[] = { + [CMN_VERSION] = {0x01, false, &common_attrs[CMN_VERSION]}, + [CMN_ACCESS] = {0x00, false, &common_attrs[CMN_ACCESS]}, + [CMN_PRESENT_ALL] = {-1, false, &common_attrs[CMN_PRESENT_ALL]}, +}; +struct attrs plain_common[] = { + [CMN_VERSION] = {0x01, false, &common_attrs[CMN_VERSION]}, +}; + +struct base_attrs portly_attrs[] = +{ + [SFP_PRESENT] = {"module_present", S_IRUGO, show_bit, NULL}, + [SFP_RESET] = {"module_reset", S_IRUGO|S_IWUGO, show_bit, set_1bit}, +}; + +struct attrs as7712_port[] = { + {0x30, true, &portly_attrs[SFP_PRESENT]}, + {0x04, true, &portly_attrs[SFP_RESET]}, +}; + +struct attrs as7816_port[] = { + {0x70, true, &portly_attrs[SFP_PRESENT]}, + {0x40, true, &portly_attrs[SFP_RESET]}, +}; + +struct attrs *as7712_cmn_list[] = { + &as7712_common[CMN_VERSION], + &as7712_common[CMN_ACCESS], + &as7712_common[CMN_PRESENT_ALL], + NULL +}; + +struct attrs *as7816_cmn_list[] = { + &as7816_common[CMN_VERSION], + &as7816_common[CMN_ACCESS], + &as7816_common[CMN_PRESENT_ALL], + NULL +}; + +struct attrs *as7312_cmn_list[] = { + &as7312_common[CMN_VERSION], + &as7312_common[CMN_ACCESS], + &as7312_common[CMN_PRESENT_ALL], + NULL +}; + +struct attrs *plain_cmn_list[] = { + &plain_common[CMN_VERSION], + NULL +}; + +struct attrs *as7712_port_list[] = { + &as7712_port[SFP_PRESENT], + &as7712_port[SFP_RESET], + NULL +}; +struct attrs *as7816_port_list[] = { + &as7816_port[SFP_PRESENT], + &as7816_port[SFP_RESET], + NULL +}; + +struct model_attrs models_attr[NUM_MODEL] = { + {.cmn = as7712_cmn_list, .portly=as7712_port_list}, + {.cmn = as7712_cmn_list, .portly=as7712_port_list}, /*7716's as 7712*/ + {.cmn = as7816_cmn_list, .portly=as7816_port_list}, + {.cmn = as7312_cmn_list, .portly=as7816_port_list}, + {.cmn = plain_cmn_list, .portly=NULL}, +}; + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; +/* Addresses scanned for accton_i2c_cpld + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +static int get_sfp_spec(int model, u16 *num, u8 *types) +{ + switch (model) { + case AS7712_32X: + case AS7716_32X: + *num = 32; + *types = HAS_QSFP; + break; + case AS7816_64X: + *num = 64; + *types = HAS_QSFP; + break; + case AS7312_54X: + *num = 54; + *types = HAS_QSFP|HAS_SFP; + default: + *types = 0; + *num = 0; + break; + } + + return 0; +} + +static int get_present_reg(int model, u8 port, u8 *cpld_addr, u8 *reg, u8 *num) +{ + u8 cpld_address[] = CPLD_ADDRS; + + switch (model) { + case AS7312_54X: + if (port < 48) { + *cpld_addr = cpld_address[1 + port/24]; + *reg = 0x09 + (port%24)/8; + *num = 8; + } + else + { + *reg = 0x18; + *num = 4; + *cpld_addr = ( port < 52)? cpld_address[1]: cpld_address[2]; + } + break; + default: + return -EINVAL; + } +} + + +/*Assume the bits for ports are listed in-a-row.*/ +static int get_reg_bit(u8 reg_start, int port, + u8 *reg ,u8 *mask) +{ + *reg = reg_start + ((port)/8); + *mask = 1 << ((port)%8); + + return 0; +} + +static int cpld_write_internal( + struct i2c_client *client, u8 reg, u8 value) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_write_byte_data(client, reg, value); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static int cpld_read_internal(struct i2c_client *client, u8 reg) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, reg); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + + +/*Turn a numberic array into string with " " between each element. + * e.g., {0x11, 0x33, 0xff, 0xf1} => "11 33 ff f1" + */ +static ssize_t array_stringify(char *buf, u8 *input, size_t size) { + + int i; + char t[MAX_RESP_LENGTH+1]; + + buf[0] = '\0'; + for (i = 0; i < size; i++) { + snprintf(t, MAX_RESP_LENGTH, "%x ", input[i]); + strncat(buf, t, MAX_RESP_LENGTH); + } + + if (strlen(buf) > 0) + buf[strlen(buf)-1] = '\0'; /*Remove tailing blank*/ + + return snprintf(buf, MAX_RESP_LENGTH, "%s\n", buf); +} + +static ssize_t show_presnet_all_distinct(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 i, value, reg; + u8 cpld_addr, num; + u8 _value[8]; + u64 *values = (u64 *)_value; + + values = 0; + mutex_lock(&data->update_lock); + while(i < data->sfp_num) + { + get_present_reg(data->model, i, &cpld_addr, ®, &num); + if(cpld_addr == client->addr) + value = cpld_read_internal(client, reg); + else + value = accton_i2c_cpld_read(cpld_addr, reg); + + if (unlikely(value < 0)) { + goto exit; + } + + *values |= (value&((1<<(num))-1)) << i; + i += num; + } + mutex_unlock(&data->update_lock); + + *values = cpu_to_le64(*values); + return array_stringify(buf, _value, i); +exit: + mutex_unlock(&data->update_lock); + return value; +} + +static ssize_t show_presnet_all(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + struct cpld_sensor *sensor = to_cpld_sensor(devattr); + u8 i, values[MAX_RESP_LENGTH/8]; + + if (sensor->reg < 0) { + return show_presnet_all_distinct(dev, devattr, buf); + } + + mutex_lock(&data->update_lock); + for (i = 0; i < ((data->sfp_num+7)/8); i++) { + values[i] = cpld_read_internal(client, sensor->reg + i); + if (unlikely(values[i] < 0)) { + goto exit; + } + } + mutex_unlock(&data->update_lock); + return array_stringify(buf, values, i); + +exit: + mutex_unlock(&data->update_lock); + return values[i]; +} + +static ssize_t show_bit(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int value; + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + struct cpld_sensor *sensor = to_cpld_sensor(devattr); + + mutex_lock(&data->update_lock); + value = cpld_read_internal(client, sensor->reg); + value = value & sensor->mask; + if (sensor->invert) + value = !value; + mutex_unlock(&data->update_lock); + + return snprintf(buf, PAGE_SIZE, "%x\n", value); +} + +static ssize_t set_1bit(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + long is_reset; + int value, status; + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + struct cpld_sensor *sensor = to_cpld_sensor(devattr); + u8 cpld_bit, reg; + + status = kstrtol(buf, 10, &is_reset); + if (status) { + return status; + } + reg = sensor->reg; + cpld_bit = sensor->mask; + mutex_lock(&data->update_lock); + value = cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + + if (sensor->invert) + is_reset = !is_reset; + + if (is_reset) { + value |= cpld_bit; + } + else { + value &= ~cpld_bit; + } + + status = cpld_write_internal(client, reg, value); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t set_byte(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + return access(dev, da, buf, count); +} + +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int status; + u32 addr, val; + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + + if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) { + return -EINVAL; + } + + if (addr > 0xFF || val > 0xFF) { + return -EINVAL; + } + + mutex_lock(&data->update_lock); + status = cpld_write_internal(client, addr, val); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static void accton_i2c_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = + kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", + client->addr); + return; + } + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void accton_i2c_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + + mutex_unlock(&list_lock); +} + +static int cpld_add_attribute(struct cpld_data *data, struct attribute *attr) +{ + int new_max_attrs = ++data->num_attributes + ATTR_ALLOC_SIZE; + void *new_attrs = krealloc(data->group.attrs, + new_max_attrs * sizeof(void *), + GFP_KERNEL); + if (!new_attrs) + return -ENOMEM; + data->group.attrs = new_attrs; + + + data->group.attrs[data->num_attributes-1] = attr; + data->group.attrs[data->num_attributes] = NULL; + + return 0; +} + +static void cpld_dev_attr_init(struct device_attribute *dev_attr, + const char *name, umode_t mode, + show_func show, store_func store) +{ + sysfs_attr_init(&dev_attr->attr); + dev_attr->attr.name = name; + dev_attr->attr.mode = mode; + dev_attr->show = show; + dev_attr->store = store; +} + +static struct cpld_sensor * add_sensor(struct cpld_data *data, + const char *name, + u8 reg, u8 mask, bool invert, + bool update, umode_t mode, + show_func get, store_func set) +{ + struct cpld_sensor *sensor; + struct device_attribute *a; + + sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return NULL; + a = &sensor->attribute; + + snprintf(sensor->name, sizeof(sensor->name), name); + sensor->reg = reg; + sensor->mask = mask; + sensor->update = update; + sensor->invert = invert; + cpld_dev_attr_init(a, sensor->name, + mode, + get, set); + + if (cpld_add_attribute(data, &a->attr)) + return NULL; + + sensor->next = data->sensors; + data->sensors = sensor; + + return sensor; +} + +static int add_attributes_cmn(struct cpld_data *data, struct attrs **cmn) +{ + u8 reg, i ; + bool invert; + struct attrs *a; + struct base_attrs *b; + + if (NULL == cmn) + return -1; + + for (i = 0; cmn[i]; i++) + { + a = cmn[i]; + + reg = a->reg; + invert = a->invert; + + b = a->base; + if (NULL == b) + break; + + if (add_sensor(data, b->name, + reg, 0xff, invert, + true, b->mode, + b->get, b->set) == NULL) + { + return -ENOMEM; + } + } + return 0; +} + +static int add_attributes_portly(struct cpld_data *data, struct attrs **pa) +{ + char name[NAME_SIZE+1]; + int i, j; + u8 reg, mask, invert; + struct attrs *a; + struct base_attrs *b; + + if (NULL == pa) + return -1; + + + for (i = 0; pa[i]; i++) { + a = pa[i]; + + invert = a->invert; + b = a->base; + if (b == NULL) + break; + + for (j = 0; j < data->sfp_num; j++) + { + snprintf(name, NAME_SIZE, "%s_%d", b->name, j+1); + get_reg_bit(a->reg, j, ®, &mask); + + if (add_sensor(data, name, reg, mask, invert, + true, b->mode, b->get, b->set) == NULL) + { + return -ENOMEM; + } + } + } + return 0; +} + +static int add_attributes(struct i2c_client *client, + struct cpld_data *data) +{ + struct model_attrs *m = data->cmn_attr; + + if (m == NULL) + return -EINVAL; + + /* Common attributes.*/ + add_attributes_cmn(data, m->cmn); + + /* Port-wise attributes.*/ + add_attributes_portly(data, m->portly); + + return 0; +} + +static int accton_i2c_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + struct cpld_data *data = NULL; + struct device *dev = &client->dev; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_dbg(dev, "i2c_check_functionality failed (0x%x)\n", client->addr); + return -EIO; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) { + return -ENOMEM; + } + + data->model = dev_id->driver_data; + data->cmn_attr = &models_attr[data->model]; + get_sfp_spec(data->model, &data->sfp_num, &data->sfp_types); + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->dev = dev; + dev_info(dev, "chip found\n"); + + status = add_attributes(client, data); + if (status) + goto out_kfree; + + /* + * If there are no attributes, something is wrong. + * Bail out instead of trying to register nothing. + */ + if (!data->num_attributes) { + dev_err(dev, "No attributes found\n"); + status = -ENODEV; + goto out_kfree; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &data->group); + if (status) { + goto out_kfree; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + accton_i2c_cpld_add_client(client); + dev_info(dev, "%s: cpld '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; +exit_remove: + sysfs_remove_group(&client->dev.kobj, &data->group); +out_kfree: + kfree(data->group.attrs); + return status; + +} + +static int accton_i2c_cpld_remove(struct i2c_client *client) +{ + struct cpld_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &data->group); + kfree(data->group.attrs); + accton_i2c_cpld_remove_client(client); + return 0; +} + +int accton_i2c_cpld_read(u8 cpld_addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = i2c_smbus_read_byte_data(cpld_node->client, reg); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(accton_i2c_cpld_read); + +int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = i2c_smbus_write_byte_data(cpld_node->client, reg, value); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(accton_i2c_cpld_write); + + +static const struct i2c_device_id accton_i2c_cpld_id[] = { + { "cpld_as7712", AS7712_32X}, + { "cpld_as7716", AS7716_32X}, + { "cpld_as7816", AS7816_64X}, + { "cpld_as7312", AS7312_54X}, + { "cpld_plain", PLAIN_CPLD}, + { }, +}; +MODULE_DEVICE_TABLE(i2c, accton_i2c_cpld_id); + +static struct i2c_driver accton_i2c_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "accton_i2c_cpld", + }, + .probe = accton_i2c_cpld_probe, + .remove = accton_i2c_cpld_remove, + .id_table = accton_i2c_cpld_id, + .address_list = normal_i2c, +}; + + +static int __init accton_i2c_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&accton_i2c_cpld_driver); +} + +static void __exit accton_i2c_cpld_exit(void) +{ + i2c_del_driver(&accton_i2c_cpld_driver); +} + +module_init(accton_i2c_cpld_init); +module_exit(accton_i2c_cpld_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_i2c_cpld driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/common/modules/accton_pmbus_3y.c b/platform/broadcom/sonic-platform-modules-accton/common/modules/accton_pmbus_3y.c new file mode 100644 index 000000000000..ed176189cdcd --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/common/modules/accton_pmbus_3y.c @@ -0,0 +1,1978 @@ +/* + * Hardware monitoring driver for 3Y power devices. + * + * Copyright (C) 2018 Accton Technology Corporation. + * Roy Lee + * + * Based on pmbus.c, pmbus.h and pmbus_core.c. + * Copyright 2012 Guenter Roeck/Ericsson AB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmbus.h" + + +enum chips { + YM2651, + YM2401, + YM2851, + LIMITED_CHIPS, +}; + + +/* + * Number of additional attribute pointers to allocate + * with each call to krealloc + */ +#define PMBUS_ATTR_ALLOC_SIZE 32 + +/* + * Index into status register array, per status register group + */ +#define PB_STATUS_BASE 0 +#define PB_STATUS_VOUT_BASE (PB_STATUS_BASE + PMBUS_PAGES) +#define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES) +#define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES) +#define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES) +#define PB_STATUS_TEMP_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES) +#define PB_STATUS_INPUT_BASE (PB_STATUS_TEMP_BASE + PMBUS_PAGES) +#define PB_STATUS_VMON_BASE (PB_STATUS_INPUT_BASE + 1) + +#define PB_NUM_STATUS_REG (PB_STATUS_VMON_BASE + 1) + +#define PMBUS_NAME_SIZE 24 + +struct pmbus_sensor { + struct pmbus_sensor *next; + char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ + struct device_attribute attribute; + u8 page; /* page number */ + u16 reg; /* register */ + enum pmbus_sensor_classes class; /* sensor class */ + bool update; /* runtime sensor update needed */ + int data; /* Sensor data. + Negative if there was a read error */ +}; +#define to_pmbus_sensor(_attr) \ + container_of(_attr, struct pmbus_sensor, attribute) + +struct pmbus_boolean { + char name[PMBUS_NAME_SIZE]; /* sysfs boolean name */ + struct sensor_device_attribute attribute; + struct pmbus_sensor *s1; + struct pmbus_sensor *s2; +}; +#define to_pmbus_boolean(_attr) \ + container_of(_attr, struct pmbus_boolean, attribute) + +struct pmbus_label { + char name[PMBUS_NAME_SIZE]; /* sysfs label name */ + struct device_attribute attribute; + char label[PMBUS_NAME_SIZE]; /* label */ +}; +#define to_pmbus_label(_attr) \ + container_of(_attr, struct pmbus_label, attribute) + +struct pmbus_data { + struct device *dev; + struct device *hwmon_dev; + + u32 flags; /* from platform data */ + + int exponent[PMBUS_PAGES]; + /* linear mode: exponent for output voltages */ + + const struct pmbus_driver_info *info; + + int max_attributes; + int num_attributes; + struct attribute_group group; + const struct attribute_group *groups[2]; + + struct pmbus_sensor *sensors; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* + * A single status register covers multiple attributes, + * so we keep them all together. + */ + u8 status[PB_NUM_STATUS_REG]; + u8 status_register; + + u8 currpage; + bool linear_16; +}; + + +static int limited_models(const struct i2c_device_id *id); + +int _pmbus_set_page(struct i2c_client *client, u8 page) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int rv = 0; + int newpage; + + if (page != data->currpage) { + rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE); + if (newpage != page) + rv = -EIO; + else + data->currpage = page; + } + return 0; +} + +/* + * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_byte(struct i2c_client *client, int page, u8 value) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->write_byte) { + status = info->write_byte(client, page, value); + if (status != -ENODATA) + return status; + } + /*Ignore page*/ + return i2c_smbus_write_byte(client, value);; +} + +/* + * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + /*Ignore page*/ + return i2c_smbus_write_word_data(client, reg, word); +} + +/* + * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg) +{ + /*Ignore page*/ + return i2c_smbus_read_word_data(client, reg);; +} + +/* + * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) +{ + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + + /*Ignore page*/ + return i2c_smbus_read_byte_data(client, reg); +} + +static void pmbus_clear_fault_page(struct i2c_client *client, int page) +{ + _pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); +} + + +static int pmbus_check_status_cml(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int status, status2; + + status = _pmbus_read_byte_data(client, -1, data->status_register); + if (status < 0 || (status & PB_STATUS_CML)) { + status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); + if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND)) + return -EIO; + } + return 0; +} + +static bool pmbus_check_register(struct i2c_client *client, + int (*func)(struct i2c_client *client, + int page, int reg), + int page, int reg) +{ + int rv; + struct pmbus_data *data = i2c_get_clientdata(client); + + if((data->flags & PMBUS_SKIP_STATUS_CHECK)) + return 1; + + rv = func(client, page, reg); + if (rv >= 0) + rv = pmbus_check_status_cml(client); + pmbus_clear_fault_page(client, -1); + return rv >= 0; +} + +static struct _pmbus_status { + u32 func; + u16 base; + u16 reg; +} pmbus_status[] = { + { PMBUS_HAVE_STATUS_VOUT, PB_STATUS_VOUT_BASE, PMBUS_STATUS_VOUT }, + { PMBUS_HAVE_STATUS_IOUT, PB_STATUS_IOUT_BASE, PMBUS_STATUS_IOUT }, + { PMBUS_HAVE_STATUS_TEMP, PB_STATUS_TEMP_BASE, + PMBUS_STATUS_TEMPERATURE + }, + { PMBUS_HAVE_STATUS_FAN12, PB_STATUS_FAN_BASE, PMBUS_STATUS_FAN_12 }, + { PMBUS_HAVE_STATUS_FAN34, PB_STATUS_FAN34_BASE, PMBUS_STATUS_FAN_34 }, +}; + +void _pmbus_clear_faults(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int i; + + for (i = 0; i < data->info->pages; i++) + pmbus_clear_fault_page(client, i); +} +static struct pmbus_data *pmbus_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + struct pmbus_sensor *sensor; + + mutex_lock(&data->update_lock); + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + int i, j; + + for (i = 0; i < info->pages; i++) { + data->status[PB_STATUS_BASE + i] + = _pmbus_read_byte_data(client, i, + data->status_register); + for (j = 0; j < ARRAY_SIZE(pmbus_status); j++) { + struct _pmbus_status *s = &pmbus_status[j]; + + if (!(info->func[i] & s->func)) + continue; + data->status[s->base + i] + = _pmbus_read_byte_data(client, i, + s->reg); + } + } + + if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) + data->status[PB_STATUS_INPUT_BASE] + = _pmbus_read_byte_data(client, 0, + PMBUS_STATUS_INPUT); + + if (info->func[0] & PMBUS_HAVE_STATUS_VMON) + data->status[PB_STATUS_VMON_BASE] + = _pmbus_read_byte_data(client, 0, + PMBUS_VIRT_STATUS_VMON); + + for (sensor = data->sensors; sensor; sensor = sensor->next) { + if (!data->valid || sensor->update) + sensor->data + = _pmbus_read_word_data(client, + sensor->page, + sensor->reg); + } + _pmbus_clear_faults(client); + data->last_updated = jiffies; + data->valid = 1; + } + mutex_unlock(&data->update_lock); + return data; +} + +/* + * Convert linear sensor values to milli- or micro-units + * depending on sensor type. + */ +static long pmbus_reg2data_linear(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + s16 exponent; + s32 mantissa; + long val; + + /* LINEAR16 */ + if ( data->linear_16 && + sensor->class == PSC_VOLTAGE_OUT) { + exponent = data->exponent[sensor->page]; + mantissa = (u16) sensor->data; + } else + { /* LINEAR11 */ + exponent = ((s16)sensor->data) >> 11; + mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5; + } + + val = mantissa; + + /* scale result to milli-units for all sensors except fans */ + if (sensor->class != PSC_FAN) + val = val * 1000L; + + /* scale result to micro-units for power sensors */ + if (sensor->class == PSC_POWER) + val = val * 1000L; + + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + return val; +} + +/* + * Convert direct sensor values to milli- or micro-units + * depending on sensor type. + */ +static long pmbus_reg2data_direct(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + long val = (s16) sensor->data; + long m, b, R; + + m = data->info->m[sensor->class]; + b = data->info->b[sensor->class]; + R = data->info->R[sensor->class]; + + if (m == 0) + return 0; + + /* X = 1/m * (Y * 10^-R - b) */ + R = -R; + /* scale result to milli-units for everything but fans */ + if (sensor->class != PSC_FAN) { + R += 3; + b *= 1000; + } + + /* scale result to micro-units for power sensors */ + if (sensor->class == PSC_POWER) { + R += 3; + b *= 1000; + } + + while (R > 0) { + val *= 10; + R--; + } + while (R < 0) { + val = DIV_ROUND_CLOSEST(val, 10); + R++; + } + + return (val - b) / m; +} + +/* + * Convert VID sensor values to milli- or micro-units + * depending on sensor type. + * We currently only support VR11. + */ +static long pmbus_reg2data_vid(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + long val = sensor->data; + + if (val < 0x02 || val > 0xb2) + return 0; + return DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100); +} + +static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) +{ + long val; + + switch (data->info->format[sensor->class]) { + case direct: + val = pmbus_reg2data_direct(data, sensor); + break; + case vid: + val = pmbus_reg2data_vid(data, sensor); + break; + case linear: + default: + val = pmbus_reg2data_linear(data, sensor); + break; + } + return val; +} + +#define MAX_MANTISSA (1023 * 1000) +#define MIN_MANTISSA (511 * 1000) + +static u16 pmbus_data2reg_linear(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + s16 exponent = 0, mantissa; + bool negative = false; + + /* simple case */ + if (val == 0) + return 0; + + if (data->linear_16 && + sensor->class == PSC_VOLTAGE_OUT) { + /* LINEAR16 does not support negative voltages */ + if (val < 0) + return 0; + + /* + * For a static exponents, we don't have a choice + * but to adjust the value to it. + */ + if (data->exponent[sensor->page] < 0) + val <<= -data->exponent[sensor->page]; + else + val >>= data->exponent[sensor->page]; + val = DIV_ROUND_CLOSEST(val, 1000); + return val & 0xffff; + } + + if (val < 0) { + negative = true; + val = -val; + } + + /* Power is in uW. Convert to mW before converting. */ + if (sensor->class == PSC_POWER) + val = DIV_ROUND_CLOSEST(val, 1000L); + + /* + * For simplicity, convert fan data to milli-units + * before calculating the exponent. + */ + if (sensor->class == PSC_FAN) + val = val * 1000; + + /* Reduce large mantissa until it fits into 10 bit */ + while (val >= MAX_MANTISSA && exponent < 15) { + exponent++; + val >>= 1; + } + /* Increase small mantissa to improve precision */ + while (val < MIN_MANTISSA && exponent > -15) { + exponent--; + val <<= 1; + } + + /* Convert mantissa from milli-units to units */ + mantissa = DIV_ROUND_CLOSEST(val, 1000); + + /* Ensure that resulting number is within range */ + if (mantissa > 0x3ff) + mantissa = 0x3ff; + + /* restore sign */ + if (negative) + mantissa = -mantissa; + + /* Convert to 5 bit exponent, 11 bit mantissa */ + return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); +} + +static u16 pmbus_data2reg_direct(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + long m, b, R; + + m = data->info->m[sensor->class]; + b = data->info->b[sensor->class]; + R = data->info->R[sensor->class]; + + /* Power is in uW. Adjust R and b. */ + if (sensor->class == PSC_POWER) { + R -= 3; + b *= 1000; + } + + /* Calculate Y = (m * X + b) * 10^R */ + if (sensor->class != PSC_FAN) { + R -= 3; /* Adjust R and b for data in milli-units */ + b *= 1000; + } + val = val * m + b; + + while (R > 0) { + val *= 10; + R--; + } + while (R < 0) { + val = DIV_ROUND_CLOSEST(val, 10); + R++; + } + + return val; +} + +static u16 pmbus_data2reg_vid(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + val = clamp_val(val, 500, 1600); + + return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625); +} + +static u16 pmbus_data2reg(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + u16 regval; + + switch (data->info->format[sensor->class]) { + case direct: + regval = pmbus_data2reg_direct(data, sensor, val); + break; + case vid: + regval = pmbus_data2reg_vid(data, sensor, val); + break; + case linear: + default: + regval = pmbus_data2reg_linear(data, sensor, val); + break; + } + return regval; +} + +/* + * Return boolean calculated from converted data. + * defines a status register index and mask. + * The mask is in the lower 8 bits, the register index is in bits 8..23. + * + * The associated pmbus_boolean structure contains optional pointers to two + * sensor attributes. If specified, those attributes are compared against each + * other to determine if a limit has been exceeded. + * + * If the sensor attribute pointers are NULL, the function returns true if + * (status[reg] & mask) is true. + * + * If sensor attribute pointers are provided, a comparison against a specified + * limit has to be performed to determine the boolean result. + * In this case, the function returns true if v1 >= v2 (where v1 and v2 are + * sensor values referenced by sensor attribute pointers s1 and s2). + * + * To determine if an object exceeds upper limits, specify = . + * To determine if an object exceeds lower limits, specify = . + * + * If a negative value is stored in any of the referenced registers, this value + * reflects an error code which will be returned. + */ +static int pmbus_get_boolean(struct pmbus_data *data, struct pmbus_boolean *b, + int index) +{ + struct pmbus_sensor *s1 = b->s1; + struct pmbus_sensor *s2 = b->s2; + u16 reg = (index >> 8) & 0xffff; + u8 mask = index & 0xff; + int ret, status; + u8 regval; + + status = data->status[reg]; + if (status < 0) + return status; + + regval = status & mask; + if (!s1 && !s2) { + ret = !!regval; + } else if (!s1 || !s2) { + WARN(1, "Bad boolean descriptor %p: s1=%p, s2=%p\n", b, s1, s2); + return 0; + } else { + long v1, v2; + + if (s1->data < 0) + return s1->data; + if (s2->data < 0) + return s2->data; + + v1 = pmbus_reg2data(data, s1); + v2 = pmbus_reg2data(data, s2); + ret = !!(regval && v1 >= v2); + } + return ret; +} + +static ssize_t pmbus_show_boolean(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct pmbus_boolean *boolean = to_pmbus_boolean(attr); + struct pmbus_data *data = pmbus_update_device(dev); + int val; + + val = pmbus_get_boolean(data, boolean, attr->index); + if (val < 0) { + return snprintf(buf, PAGE_SIZE, "%d\n", 1); + } + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t pmbus_show_sensor(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pmbus_data *data = pmbus_update_device(dev); + struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); + + + if (sensor->data < 0) { + return snprintf(buf, PAGE_SIZE, "%d\n", 0); + } + + + return snprintf(buf, PAGE_SIZE, "%ld\n", pmbus_reg2data(data, sensor)); +} + +static ssize_t pmbus_set_sensor(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); + ssize_t rv = count; + long val = 0; + int ret; + u16 regval; + + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + regval = pmbus_data2reg(data, sensor, val); + ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval); + if (ret < 0) + rv = ret; + else + sensor->data = regval; + mutex_unlock(&data->update_lock); + return rv; +} + +static ssize_t pmbus_show_label(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct pmbus_label *label = to_pmbus_label(da); + + return snprintf(buf, PAGE_SIZE, "%s\n", label->label); +} + +static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr) +{ + if (data->num_attributes >= data->max_attributes - 1) { + int new_max_attrs = data->max_attributes + PMBUS_ATTR_ALLOC_SIZE; + void *new_attrs = krealloc(data->group.attrs, + new_max_attrs * sizeof(void *), + GFP_KERNEL); + if (!new_attrs) + return -ENOMEM; + data->group.attrs = new_attrs; + data->max_attributes = new_max_attrs; + } + + + + data->group.attrs[data->num_attributes++] = attr; + data->group.attrs[data->num_attributes] = NULL; + return 0; +} + +static void pmbus_dev_attr_init(struct device_attribute *dev_attr, + const char *name, + umode_t mode, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count)) +{ + sysfs_attr_init(&dev_attr->attr); + dev_attr->attr.name = name; + dev_attr->attr.mode = mode; + dev_attr->show = show; + dev_attr->store = store; +} + +static void pmbus_attr_init(struct sensor_device_attribute *a, + const char *name, + umode_t mode, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count), + int idx) +{ + pmbus_dev_attr_init(&a->dev_attr, name, mode, show, store); + a->index = idx; +} + +static int pmbus_add_boolean(struct pmbus_data *data, + const char *name, const char *type, int seq, + struct pmbus_sensor *s1, + struct pmbus_sensor *s2, + u16 reg, u8 mask) +{ + struct pmbus_boolean *boolean; + struct sensor_device_attribute *a; + + boolean = devm_kzalloc(data->dev, sizeof(*boolean), GFP_KERNEL); + if (!boolean) + return -ENOMEM; + + a = &boolean->attribute; + + snprintf(boolean->name, sizeof(boolean->name), "%s%d_%s", + name, seq, type); + boolean->s1 = s1; + boolean->s2 = s2; + pmbus_attr_init(a, boolean->name, S_IRUGO, pmbus_show_boolean, NULL, + (reg << 8) | mask); + + return pmbus_add_attribute(data, &a->dev_attr.attr); +} + +static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, + const char *name, const char *type, + int seq, int page, int reg, + enum pmbus_sensor_classes class, + bool update, bool readonly) +{ + struct pmbus_sensor *sensor; + struct device_attribute *a; + + sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return NULL; + a = &sensor->attribute; + + snprintf(sensor->name, sizeof(sensor->name), "%s%d_%s", + name, seq, type); + sensor->page = page; + sensor->reg = reg; + sensor->class = class; + sensor->update = update; + pmbus_dev_attr_init(a, sensor->name, + readonly ? S_IRUGO : S_IRUGO | S_IWUSR, + pmbus_show_sensor, pmbus_set_sensor); + + if (pmbus_add_attribute(data, &a->attr)) + return NULL; + + sensor->next = data->sensors; + data->sensors = sensor; + + return sensor; +} + +static int pmbus_add_label(struct pmbus_data *data, + const char *name, int seq, + const char *lstring, int index) +{ + struct pmbus_label *label; + struct device_attribute *a; + + label = devm_kzalloc(data->dev, sizeof(*label), GFP_KERNEL); + if (!label) + return -ENOMEM; + + a = &label->attribute; + + snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq); + if (!index) + strncpy(label->label, lstring, sizeof(label->label) - 1); + else + snprintf(label->label, sizeof(label->label), "%s%d", lstring, + index); + + pmbus_dev_attr_init(a, label->name, S_IRUGO, pmbus_show_label, NULL); + return pmbus_add_attribute(data, &a->attr); +} + +/* + * Search for attributes. Allocate sensors, booleans, and labels as needed. + */ + +/* + * The pmbus_limit_attr structure describes a single limit attribute + * and its associated alarm attribute. + */ +struct pmbus_limit_attr { + u16 reg; /* Limit register */ + u16 sbit; /* Alarm attribute status bit */ + bool update; /* True if register needs updates */ + bool low; /* True if low limit; for limits with compare + functions only */ + const char *attr; /* Attribute name */ + const char *alarm; /* Alarm attribute name */ +}; + +/* + * The pmbus_sensor_attr structure describes one sensor attribute. This + * description includes a reference to the associated limit attributes. + */ +struct pmbus_sensor_attr { + u16 reg; /* sensor register */ + u8 gbit; /* generic status bit */ + u8 nlimit; /* # of limit registers */ + enum pmbus_sensor_classes class;/* sensor class */ + const char *label; /* sensor label */ + bool paged; /* true if paged sensor */ + bool update; /* true if update needed */ + bool compare; /* true if compare function needed */ + u32 func; /* sensor mask */ + u32 sfunc; /* sensor status mask */ + int sbase; /* status base register */ + const struct pmbus_limit_attr *limit;/* limit registers */ +}; + +/* + * Add a set of limit attributes and, if supported, the associated + * alarm attributes. + * returns 0 if no alarm register found, 1 if an alarm register was found, + * < 0 on errors. + */ +static int pmbus_add_limit_attrs(struct i2c_client *client, + struct pmbus_data *data, + const struct pmbus_driver_info *info, + const char *name, int index, int page, + struct pmbus_sensor *base, + const struct pmbus_sensor_attr *attr) +{ + const struct pmbus_limit_attr *l = attr->limit; + int nlimit = attr->nlimit; + int have_alarm = 0; + int i, ret; + struct pmbus_sensor *curr; + + for (i = 0; i < nlimit; i++) { + if (_pmbus_check_word_register(client, page, l->reg)) + { + curr = pmbus_add_sensor(data, name, l->attr, index, + page, l->reg, attr->class, + attr->update || l->update, + false); + if (!curr) + return -ENOMEM; + if (l->sbit && (info->func[page] & attr->sfunc)) { + ret = pmbus_add_boolean(data, name, + l->alarm, index, + attr->compare ? l->low ? curr : base + : NULL, + attr->compare ? l->low ? base : curr + : NULL, + attr->sbase + page, l->sbit); + if (ret) + return ret; + have_alarm = 1; + } + } + l++; + } + return have_alarm; +} + +bool _pmbus_check_byte_register(struct i2c_client *client, int page, int reg) +{ + return pmbus_check_register(client, _pmbus_read_byte_data, page, reg); +} + + +bool _pmbus_check_word_register(struct i2c_client *client, int page, int reg) +{ + return pmbus_check_register(client, _pmbus_read_word_data, page, reg); +} + +static int pmbus_add_sensor_attrs_one(struct i2c_client *client, + struct pmbus_data *data, + const struct pmbus_driver_info *info, + const char *name, + int index, int page, + const struct pmbus_sensor_attr *attr) +{ + struct pmbus_sensor *base; + int ret; + + if (attr->label) { + ret = pmbus_add_label(data, name, index, attr->label, + attr->paged ? page + 1 : 0); + if (ret) + return ret; + } + base = pmbus_add_sensor(data, name, "input", index, page, attr->reg, + attr->class, true, true); + + + if (!base) + return -ENOMEM; + + if (attr->sfunc) { + ret = pmbus_add_limit_attrs(client, data, info, name, + index, page, base, attr); + if (ret < 0) + return ret; + /* + * Add generic alarm attribute only if there are no individual + * alarm attributes, if there is a global alarm bit, and if + * the generic status register for this page is accessible. + */ + if (!ret && attr->gbit && + _pmbus_check_byte_register(client, page, + data->status_register)) { + ret = pmbus_add_boolean(data, name, "alarm", index, + NULL, NULL, + PB_STATUS_BASE + page, + attr->gbit); + if (ret) + return ret; + } + } + return 0; +} + +static int pmbus_add_sensor_attrs(struct i2c_client *client, + struct pmbus_data *data, + const char *name, + const struct pmbus_sensor_attr *attrs, + int nattrs) +{ + const struct pmbus_driver_info *info = data->info; + int index, i; + int ret; + + index = 1; + for (i = 0; i < nattrs; i++) { + int page, pages; + + pages = attrs->paged ? info->pages : 1; + for (page = 0; page < pages; page++) { + if (!(info->func[page] & attrs->func)) + continue; + ret = pmbus_add_sensor_attrs_one(client, data, info, + name, index, page, + attrs); + if (ret) + return ret; + index++; + } + attrs++; + } + return 0; +} + +static const struct pmbus_limit_attr vin_limit_attrs[] = { + { + .reg = PMBUS_VIN_UV_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_VOLTAGE_UV_WARNING, + }, { + .reg = PMBUS_VIN_UV_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_VOLTAGE_UV_FAULT, + }, { + .reg = PMBUS_VIN_OV_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_VOLTAGE_OV_WARNING, + }, { + .reg = PMBUS_VIN_OV_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_VOLTAGE_OV_FAULT, + }, { + .reg = PMBUS_VIRT_READ_VIN_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_VIN_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_VIN_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_VIN_HISTORY, + .attr = "reset_history", + }, +}; + +static const struct pmbus_limit_attr vmon_limit_attrs[] = { + { + .reg = PMBUS_VIRT_VMON_UV_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_VOLTAGE_UV_WARNING, + }, { + .reg = PMBUS_VIRT_VMON_UV_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_VOLTAGE_UV_FAULT, + }, { + .reg = PMBUS_VIRT_VMON_OV_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_VOLTAGE_OV_WARNING, + }, { + .reg = PMBUS_VIRT_VMON_OV_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_VOLTAGE_OV_FAULT, + } +}; + +static const struct pmbus_limit_attr vout_limit_attrs[] = { + { + .reg = PMBUS_VOUT_UV_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_VOLTAGE_UV_WARNING, + }, { + .reg = PMBUS_VOUT_UV_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_VOLTAGE_UV_FAULT, + }, { + .reg = PMBUS_VOUT_OV_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_VOLTAGE_OV_WARNING, + }, { + .reg = PMBUS_VOUT_OV_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_VOLTAGE_OV_FAULT, + }, { + .reg = PMBUS_VIRT_READ_VOUT_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_VOUT_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_VOUT_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_VOUT_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_sensor_attr voltage_attributes[] = { + { + .reg = PMBUS_READ_VIN, + .class = PSC_VOLTAGE_IN, + .label = "vin", + .func = PMBUS_HAVE_VIN, + .sfunc = PMBUS_HAVE_STATUS_INPUT, + .sbase = PB_STATUS_INPUT_BASE, + .gbit = PB_STATUS_VIN_UV, + .limit = vin_limit_attrs, + .nlimit = ARRAY_SIZE(vin_limit_attrs), + }, { + .reg = PMBUS_VIRT_READ_VMON, + .class = PSC_VOLTAGE_IN, + .label = "vmon", + .func = PMBUS_HAVE_VMON, + .sfunc = PMBUS_HAVE_STATUS_VMON, + .sbase = PB_STATUS_VMON_BASE, + .limit = vmon_limit_attrs, + .nlimit = ARRAY_SIZE(vmon_limit_attrs), + }, { + .reg = PMBUS_READ_VCAP, + .class = PSC_VOLTAGE_IN, + .label = "vcap", + .func = PMBUS_HAVE_VCAP, + }, { + .reg = PMBUS_READ_VOUT, + .class = PSC_VOLTAGE_OUT, + .label = "vout", + .paged = true, + .func = PMBUS_HAVE_VOUT, + .sfunc = PMBUS_HAVE_STATUS_VOUT, + .sbase = PB_STATUS_VOUT_BASE, + .gbit = PB_STATUS_VOUT_OV, + .limit = vout_limit_attrs, + .nlimit = ARRAY_SIZE(vout_limit_attrs), + } +}; + +/* Current attributes */ + +static const struct pmbus_limit_attr iin_limit_attrs[] = { + { + .reg = PMBUS_IIN_OC_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_IIN_OC_WARNING, + }, { + .reg = PMBUS_IIN_OC_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_IIN_OC_FAULT, + }, { + .reg = PMBUS_VIRT_READ_IIN_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_IIN_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_IIN_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_IIN_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_limit_attr iout_limit_attrs[] = { + { + .reg = PMBUS_IOUT_OC_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_IOUT_OC_WARNING, + }, { + .reg = PMBUS_IOUT_UC_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_IOUT_UC_FAULT, + }, { + .reg = PMBUS_IOUT_OC_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_IOUT_OC_FAULT, + }, { + .reg = PMBUS_VIRT_READ_IOUT_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_IOUT_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_IOUT_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_IOUT_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_sensor_attr current_attributes[] = { + { + .reg = PMBUS_READ_IIN, + .class = PSC_CURRENT_IN, + .label = "iin", + .func = PMBUS_HAVE_IIN, + .sfunc = PMBUS_HAVE_STATUS_INPUT, + .sbase = PB_STATUS_INPUT_BASE, + .limit = iin_limit_attrs, + .nlimit = ARRAY_SIZE(iin_limit_attrs), + }, { + .reg = PMBUS_READ_IOUT, + .class = PSC_CURRENT_OUT, + .label = "iout", + .paged = true, + .func = PMBUS_HAVE_IOUT, + .sfunc = PMBUS_HAVE_STATUS_IOUT, + .sbase = PB_STATUS_IOUT_BASE, + .gbit = PB_STATUS_IOUT_OC, + .limit = iout_limit_attrs, + .nlimit = ARRAY_SIZE(iout_limit_attrs), + } +}; + +/* Power attributes */ + +static const struct pmbus_limit_attr pin_limit_attrs[] = { + { + .reg = PMBUS_PIN_OP_WARN_LIMIT, + .attr = "max", + .alarm = "alarm", + .sbit = PB_PIN_OP_WARNING, + }, { + .reg = PMBUS_VIRT_READ_PIN_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_PIN_MAX, + .update = true, + .attr = "input_highest", + }, { + .reg = PMBUS_VIRT_RESET_PIN_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_limit_attr pout_limit_attrs[] = { + { + .reg = PMBUS_POUT_MAX, + .attr = "cap", + .alarm = "cap_alarm", + .sbit = PB_POWER_LIMITING, + }, { + .reg = PMBUS_POUT_OP_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_POUT_OP_WARNING, + }, { + .reg = PMBUS_POUT_OP_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_POUT_OP_FAULT, + }, { + .reg = PMBUS_VIRT_READ_POUT_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_POUT_MAX, + .update = true, + .attr = "input_highest", + }, { + .reg = PMBUS_VIRT_RESET_POUT_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_sensor_attr power_attributes[] = { + { + .reg = PMBUS_READ_PIN, + .class = PSC_POWER, + .label = "pin", + .func = PMBUS_HAVE_PIN, + .sfunc = PMBUS_HAVE_STATUS_INPUT, + .sbase = PB_STATUS_INPUT_BASE, + .limit = pin_limit_attrs, + .nlimit = ARRAY_SIZE(pin_limit_attrs), + }, { + .reg = PMBUS_READ_POUT, + .class = PSC_POWER, + .label = "pout", + .paged = true, + .func = PMBUS_HAVE_POUT, + .sfunc = PMBUS_HAVE_STATUS_IOUT, + .sbase = PB_STATUS_IOUT_BASE, + .limit = pout_limit_attrs, + .nlimit = ARRAY_SIZE(pout_limit_attrs), + } +}; + +/* Temperature atributes */ + +static const struct pmbus_limit_attr temp_limit_attrs[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .low = true, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + }, { + .reg = PMBUS_VIRT_READ_TEMP_MIN, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_TEMP_AVG, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_TEMP_MAX, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_TEMP_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_limit_attr temp_limit_attrs2[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .low = true, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + }, { + .reg = PMBUS_VIRT_READ_TEMP2_MIN, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_TEMP2_AVG, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_TEMP2_MAX, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_TEMP2_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_limit_attr temp_limit_attrs3[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .low = true, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + } +}; + +static const struct pmbus_sensor_attr temp_attributes[] = { + { + .reg = PMBUS_READ_TEMPERATURE_1, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sbase = PB_STATUS_TEMP_BASE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs, + .nlimit = ARRAY_SIZE(temp_limit_attrs), + }, { + .reg = PMBUS_READ_TEMPERATURE_2, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP2, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sbase = PB_STATUS_TEMP_BASE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs2, + .nlimit = ARRAY_SIZE(temp_limit_attrs2), + }, { + .reg = PMBUS_READ_TEMPERATURE_3, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP3, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sbase = PB_STATUS_TEMP_BASE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs3, + .nlimit = ARRAY_SIZE(temp_limit_attrs3), + } +}; + +static const int pmbus_fan_registers[] = { + PMBUS_READ_FAN_SPEED_1, + PMBUS_READ_FAN_SPEED_2, + PMBUS_READ_FAN_SPEED_3, + PMBUS_READ_FAN_SPEED_4 +}; + +static const int pmbus_fan_config_registers[] = { + PMBUS_FAN_CONFIG_12, + PMBUS_FAN_CONFIG_12, + PMBUS_FAN_CONFIG_34, + PMBUS_FAN_CONFIG_34 +}; + +static const int pmbus_fan_status_registers[] = { + PMBUS_STATUS_FAN_12, + PMBUS_STATUS_FAN_12, + PMBUS_STATUS_FAN_34, + PMBUS_STATUS_FAN_34 +}; + +static const u32 pmbus_fan_flags[] = { + PMBUS_HAVE_FAN12, + PMBUS_HAVE_FAN12, + PMBUS_HAVE_FAN34, + PMBUS_HAVE_FAN34 +}; + +static const u32 pmbus_fan_status_flags[] = { + PMBUS_HAVE_STATUS_FAN12, + PMBUS_HAVE_STATUS_FAN12, + PMBUS_HAVE_STATUS_FAN34, + PMBUS_HAVE_STATUS_FAN34 +}; + +/* Fans */ +static int pmbus_add_fan_attributes(struct i2c_client *client, + struct pmbus_data *data) +{ + const struct pmbus_driver_info *info = data->info; + int index = 1; + int page; + int ret; + + for (page = 0; page < info->pages; page++) { + int f; + + for (f = 0; f < ARRAY_SIZE(pmbus_fan_registers); f++) { + int regval; + + if (f >= info->fan_num) + break; + + if (!(info->func[page] & pmbus_fan_flags[f])) + break; + + if (!_pmbus_check_word_register(client, page, + pmbus_fan_registers[f])) + break; + + /* + * Skip fan if not installed. + * Each fan configuration register covers multiple fans, + * so we have to do some magic. + */ + regval = _pmbus_read_byte_data(client, page, + pmbus_fan_config_registers[f]); + if (regval < 0 || + (!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4))))) + continue; + + if (pmbus_add_sensor(data, "fan", "input", index, + page, pmbus_fan_registers[f], + PSC_FAN, true, true) == NULL) + return -ENOMEM; + + /* + * Each fan status register covers multiple fans, + * so we have to do some magic. + */ + if ((info->func[page] & pmbus_fan_status_flags[f]) && + _pmbus_check_byte_register(client, + page, pmbus_fan_status_registers[f])) { + int base; + + if (f > 1) /* fan 3, 4 */ + base = PB_STATUS_FAN34_BASE + page; + else + base = PB_STATUS_FAN_BASE + page; + ret = pmbus_add_boolean(data, "fan", + "alarm", index, NULL, NULL, base, + PB_FAN_FAN1_WARNING >> (f & 1)); + if (ret) + return ret; + ret = pmbus_add_boolean(data, "fan", + "fault", index, NULL, NULL, base, + PB_FAN_FAN1_FAULT >> (f & 1)); + if (ret) + return ret; + } + index++; + } + } + return 0; +} + +static int pmbus_find_attributes(struct i2c_client *client, + struct pmbus_data *data) +{ + int ret; + + /* Voltage sensors */ + ret = pmbus_add_sensor_attrs(client, data, "in", voltage_attributes, + ARRAY_SIZE(voltage_attributes)); + if (ret) + return ret; + + /* Current sensors */ + ret = pmbus_add_sensor_attrs(client, data, "curr", current_attributes, + ARRAY_SIZE(current_attributes)); + if (ret) + return ret; + + /* Power sensors */ + ret = pmbus_add_sensor_attrs(client, data, "power", power_attributes, + ARRAY_SIZE(power_attributes)); + if (ret) + return ret; + + /* Temperature sensors */ + ret = pmbus_add_sensor_attrs(client, data, "temp", temp_attributes, + ARRAY_SIZE(temp_attributes)); + if (ret) + return ret; + + /* Fans */ + ret = pmbus_add_fan_attributes(client, data); + return ret; +} + +/* + * Identify chip parameters. + * This function is called for all chips. + */ +static int pmbus_identify_common(struct i2c_client *client, + struct pmbus_data *data, int page) +{ + int vout_mode = -1; + + if (_pmbus_check_byte_register(client, page, PMBUS_VOUT_MODE)) + { + vout_mode = _pmbus_read_byte_data(client, page, + PMBUS_VOUT_MODE); + } + + if (vout_mode >= 0 && vout_mode != 0xff) { + /* + * Not all chips support the VOUT_MODE command, + * so a failure to read it is not an error. + */ + switch (vout_mode >> 5) { + case 0: /* linear mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != linear) + return -ENODEV; + + data->exponent[page] = ((s8)(vout_mode << 3)) >> 3; + break; + case 1: /* VID mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != vid) + return -ENODEV; + break; + case 2: /* direct mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != direct) + return -ENODEV; + break; + default: + return -ENODEV; + } + } + + pmbus_clear_fault_page(client, page); + return 0; +} + +static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, + struct pmbus_driver_info *info) +{ + struct device *dev = &client->dev; + int page, ret; + + /* + * Some PMBus chips don't support PMBUS_STATUS_BYTE, so try + * to use PMBUS_STATUS_WORD instead if that is the case. + * Bail out if both registers are not supported. + */ + if(0) { /*Skip this for the i2c access may fail if PSU is not powered.*/ + data->status_register = PMBUS_STATUS_BYTE; + ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); + if (ret < 0 || ret == 0xff) { + data->status_register = PMBUS_STATUS_WORD; + ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); + if (ret < 0 || ret == 0xffff) { + dev_err(dev, "PMBus status register not found\n"); + return -ENODEV; + } + } + } + _pmbus_clear_faults(client); + + if (info->identify) { + ret = (*info->identify)(client, info); + if (ret < 0) { + dev_err(dev, "Chip identification failed\n"); + return ret; + } + } + + if (info->pages <= 0 || info->pages > PMBUS_PAGES) { + dev_err(dev, "Bad number of PMBus pages: %d\n", info->pages); + return -ENODEV; + } + + for (page = 0; page < info->pages; page++) { + ret = pmbus_identify_common(client, data, page); + if (ret < 0) { + dev_err(dev, "Failed to identify chip capabilities\n"); + return ret; + } + } + return 0; +} + +int _pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, + struct pmbus_driver_info *info) +{ + struct device *dev = &client->dev; + struct pmbus_data *data; + int ret; + + if (!info) + return -ENODEV; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE + | I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->dev = dev; + + if (limited_models(id)) + { + data->flags |= PMBUS_SKIP_STATUS_CHECK; + info->pages = 1; + info->fan_num = 1; + data->linear_16 = 0; + } + else + { + info->pages = id->driver_data; + info->fan_num = 4; + data->linear_16 = 1; + + } + + data->info = info; + + ret = pmbus_init_common(client, data, info); + if (ret < 0) + return ret; + + ret = pmbus_find_attributes(client, data); + if (ret) + goto out_kfree; + + /* + * If there are no attributes, something is wrong. + * Bail out instead of trying to register nothing. + */ + if (!data->num_attributes) { + dev_err(dev, "No attributes found\n"); + ret = -ENODEV; + goto out_kfree; + } + + data->groups[0] = &data->group; + data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + dev_err(dev, "Failed to register hwmon device\n"); + goto out_kfree; + } + return 0; + +out_kfree: + kfree(data->group.attrs); + return ret; +} + +/* + * Find sensor groups and status registers on each page. + */ +static void pmbus_find_sensor_groups(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int page; + + /* For i2c might not available when this driver installing, + turn on all attributes but VMON and FAN34.*/ + info->func[0] |= 0x1feff; + return ; + + + /* Sensors detected on page 0 only */ + if (_pmbus_check_word_register(client, 0, PMBUS_READ_VIN)) + info->func[0] |= PMBUS_HAVE_VIN; + if (_pmbus_check_word_register(client, 0, PMBUS_READ_VCAP)) + info->func[0] |= PMBUS_HAVE_VCAP; + if (_pmbus_check_word_register(client, 0, PMBUS_READ_IIN)) + info->func[0] |= PMBUS_HAVE_IIN; + if (_pmbus_check_word_register(client, 0, PMBUS_READ_PIN)) + info->func[0] |= PMBUS_HAVE_PIN; + if (info->func[0] + && _pmbus_check_byte_register(client, 0, PMBUS_STATUS_INPUT)) + info->func[0] |= PMBUS_HAVE_STATUS_INPUT; + if (info->fan_num > 0) + { + if (_pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_12) && + _pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_1)) { + info->func[0] |= PMBUS_HAVE_FAN12; + if (_pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_12)) + info->func[0] |= PMBUS_HAVE_STATUS_FAN12; + } + } + if (info->fan_num > 2) + { + if (_pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_34) && + _pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_3)) { + info->func[0] |= PMBUS_HAVE_FAN34; + if (_pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_34)) + info->func[0] |= PMBUS_HAVE_STATUS_FAN34; + } + } + + if (_pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_1)) + info->func[0] |= PMBUS_HAVE_TEMP; + if (_pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_2)) + info->func[0] |= PMBUS_HAVE_TEMP2; + if (_pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_3)) + info->func[0] |= PMBUS_HAVE_TEMP3; + + if (info->func[0] & (PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 + | PMBUS_HAVE_TEMP3) + && _pmbus_check_byte_register(client, 0, + PMBUS_STATUS_TEMPERATURE)) + info->func[0] |= PMBUS_HAVE_STATUS_TEMP; + + /* Sensors detected on all pages */ + for (page = 0; page < info->pages; page++) { + if (_pmbus_check_word_register(client, page, PMBUS_READ_VOUT)) { + info->func[page] |= PMBUS_HAVE_VOUT; + if (_pmbus_check_byte_register(client, page, + PMBUS_STATUS_VOUT)) + info->func[page] |= PMBUS_HAVE_STATUS_VOUT; + } + if (_pmbus_check_word_register(client, page, PMBUS_READ_IOUT)) { + info->func[page] |= PMBUS_HAVE_IOUT; + if (_pmbus_check_byte_register(client, 0, + PMBUS_STATUS_IOUT)) + info->func[page] |= PMBUS_HAVE_STATUS_IOUT; + } + if (_pmbus_check_word_register(client, page, PMBUS_READ_POUT)) + info->func[page] |= PMBUS_HAVE_POUT; + } +} + +/* + * Identify chip parameters. + */ +static int pmbus_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int ret = 0; + + if (!info->pages) { + /* + * Check if the PAGE command is supported. If it is, + * keep setting the page number until it fails or until the + * maximum number of pages has been reached. Assume that + * this is the number of pages supported by the chip. + */ + if (_pmbus_check_byte_register(client, 0, PMBUS_PAGE)) { + int page; + + for (page = 1; page < PMBUS_PAGES; page++) { + if (_pmbus_set_page(client, page) < 0) + break; + } + _pmbus_set_page(client, 0); + info->pages = page; + } else { + info->pages = 1; + } + } + + if (_pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) { + int vout_mode; + + vout_mode = _pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); + if (vout_mode >= 0 && vout_mode != 0xff) { + switch (vout_mode >> 5) { + case 0: + break; + case 1: + info->format[PSC_VOLTAGE_OUT] = vid; + break; + case 2: + info->format[PSC_VOLTAGE_OUT] = direct; + break; + default: + ret = -ENODEV; + goto abort; + } + } + } + + + /* + * We should check if the COEFFICIENTS register is supported. + * If it is, and the chip is configured for direct mode, we can read + * the coefficients from the chip, one set per group of sensor + * registers. + * + * To do this, we will need access to a chip which actually supports the + * COEFFICIENTS command, since the command is too complex to implement + * without testing it. Until then, abort if a chip configured for direct + * mode was detected. + */ + if (info->format[PSC_VOLTAGE_OUT] == direct) { + ret = -ENODEV; + goto abort; + } + + /* Try to find sensor groups */ + pmbus_find_sensor_groups(client, info); +abort: + return ret; +} + +/* + * Use driver_data to set the number of pages supported by the chip. + */ +static const struct i2c_device_id pmbus_id[] = { + { "accton_ym2651", YM2651 }, + { "accton_ym2401", YM2401 }, + { "accton_ym2851", YM2851 }, + {} +}; + +static int limited_models(const struct i2c_device_id *id) +{ + int j; + + if (!id->name) + return 0; + + for (j = 0; j < ARRAY_SIZE(pmbus_id); j++) { + if (!pmbus_id[j].name) + continue; + + if (!strcmp(id->name, pmbus_id[j].name)) + { + if(id->driver_data < LIMITED_CHIPS) + { + return 1; + } + else + { + return 0; + } + } + + } + + return 0; +} + +static int pmbus_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pmbus_driver_info *info; + + info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->identify = pmbus_identify; + + return _pmbus_do_probe(client, id, info); +} + +int _pmbus_do_remove(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); + kfree(data->group.attrs); + return 0; +} + +MODULE_DEVICE_TABLE(i2c, pmbus_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver pmbus_driver = { + .driver = { + .name = "accton_pmbus_3y", + }, + .probe = pmbus_probe, + .remove = _pmbus_do_remove, + .id_table = pmbus_id, +}; + +module_i2c_driver(pmbus_driver); + +MODULE_AUTHOR("Roy Lee"); +MODULE_DESCRIPTION("Accton PMBus driver for 3Y Power YM-2651Y."); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-accton/common/modules/cpr_4011_4mxx.c b/platform/broadcom/sonic-platform-modules-accton/common/modules/cpr_4011_4mxx.c new file mode 100644 index 000000000000..2cea5f5e39f9 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/common/modules/cpr_4011_4mxx.c @@ -0,0 +1,402 @@ +/* + * An hwmon driver for the CPR-4011-4Mxx Redundant Power Module + * + * Copyright (C) Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * 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. + */ +#if 0 +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_FAN_DUTY_CYCLE 100 + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x3c, 0x3d, 0x3e, 0x3f, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct cpr_4011_4mxx_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 vout_mode; /* Register value */ + u16 v_in; /* Register value */ + u16 v_out; /* Register value */ + u16 i_in; /* Register value */ + u16 i_out; /* Register value */ + u16 p_in; /* Register value */ + u16 p_out; /* Register value */ + u16 temp_input[2]; /* Register value */ + u8 fan_fault; /* Register value */ + u16 fan_duty_cycle[2]; /* Register value */ + u16 fan_speed[2]; /* Register value */ +}; + +static ssize_t show_linear(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_vout(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da, const char *buf, size_t count); +static int cpr_4011_4mxx_write_word(struct i2c_client *client, u8 reg, u16 value); +static struct cpr_4011_4mxx_data *cpr_4011_4mxx_update_device(struct device *dev); + +enum cpr_4011_4mxx_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, +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_v_in, S_IRUGO, show_linear, NULL, PSU_V_IN); +static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, show_vout, NULL, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(psu_i_in, S_IRUGO, show_linear, NULL, PSU_I_IN); +static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, show_linear, NULL, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(psu_p_in, S_IRUGO, show_linear, NULL, PSU_P_IN); +static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, show_linear, NULL, PSU_P_OUT); +static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_fan_fault, NULL, PSU_FAN1_FAULT); +static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IWUSR | S_IRUGO, show_linear, set_fan_duty_cycle, PSU_FAN1_DUTY_CYCLE); +static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); + +static struct attribute *cpr_4011_4mxx_attributes[] = { + &sensor_dev_attr_psu_v_in.dev_attr.attr, + &sensor_dev_attr_psu_v_out.dev_attr.attr, + &sensor_dev_attr_psu_i_in.dev_attr.attr, + &sensor_dev_attr_psu_i_out.dev_attr.attr, + &sensor_dev_attr_psu_p_in.dev_attr.attr, + &sensor_dev_attr_psu_p_out.dev_attr.attr, + &sensor_dev_attr_psu_temp1_input.dev_attr.attr, + &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, + &sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr, + &sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr, + NULL +}; + +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(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpr_4011_4mxx_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; + + mutex_lock(&data->update_lock); + data->fan_duty_cycle[nr] = speed; + cpr_4011_4mxx_write_word(client, 0x3B + nr, data->fan_duty_cycle[nr]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_linear(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpr_4011_4mxx_data *data = cpr_4011_4mxx_update_device(dev); + + u16 value = 0; + int exponent, mantissa; + int multiplier = 1000; + + switch (attr->index) { + case PSU_V_IN: + value = data->v_in; + break; + case PSU_I_IN: + value = data->i_in; + break; + case PSU_I_OUT: + value = data->i_out; + break; + case PSU_P_IN: + value = data->p_in; + break; + case PSU_P_OUT: + value = data->p_out; + break; + case PSU_TEMP1_INPUT: + value = data->temp_input[0]; + break; + case PSU_FAN1_DUTY_CYCLE: + multiplier = 1; + value = data->fan_duty_cycle[0]; + break; + case PSU_FAN1_SPEED: + multiplier = 1; + value = data->fan_speed[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 show_fan_fault(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpr_4011_4mxx_data *data = cpr_4011_4mxx_update_device(dev); + + u8 shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6; + + return sprintf(buf, "%d\n", data->fan_fault >> shift); +} + +static ssize_t show_vout(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct cpr_4011_4mxx_data *data = cpr_4011_4mxx_update_device(dev); + int exponent, mantissa; + int multiplier = 1000; + + exponent = two_complement_to_int(data->vout_mode, 5, 0x1f); + mantissa = data->v_out; + + return (exponent > 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) : + sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); +} + +static const struct attribute_group cpr_4011_4mxx_group = { + .attrs = cpr_4011_4mxx_attributes, +}; + +static int cpr_4011_4mxx_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct cpr_4011_4mxx_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(struct cpr_4011_4mxx_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, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &cpr_4011_4mxx_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &cpr_4011_4mxx_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int cpr_4011_4mxx_remove(struct i2c_client *client) +{ + struct cpr_4011_4mxx_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &cpr_4011_4mxx_group); + kfree(data); + + return 0; +} + +static const struct i2c_device_id cpr_4011_4mxx_id[] = { + { "cpr_4011_4mxx", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, cpr_4011_4mxx_id); + +static struct i2c_driver cpr_4011_4mxx_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "cpr_4011_4mxx", + }, + .probe = cpr_4011_4mxx_probe, + .remove = cpr_4011_4mxx_remove, + .id_table = cpr_4011_4mxx_id, + .address_list = normal_i2c, +}; + +static int cpr_4011_4mxx_read_byte(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int cpr_4011_4mxx_read_word(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_data(client, reg); +} + +static int cpr_4011_4mxx_write_word(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_word_data(client, reg, value); +} + +struct reg_data_byte { + u8 reg; + u8 *value; +}; + +struct reg_data_word { + u8 reg; + u16 *value; +}; + +static struct cpr_4011_4mxx_data *cpr_4011_4mxx_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpr_4011_4mxx_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int i, status; + struct reg_data_byte regs_byte[] = { {0x20, &data->vout_mode}, + {0x81, &data->fan_fault} + }; + struct reg_data_word regs_word[] = { {0x88, &data->v_in}, + {0x8b, &data->v_out}, + {0x89, &data->i_in}, + {0x8c, &data->i_out}, + {0x96, &data->p_out}, + {0x97, &data->p_in}, + {0x8d, &(data->temp_input[0])}, + {0x8e, &(data->temp_input[1])}, + {0x3b, &(data->fan_duty_cycle[0])}, + {0x3c, &(data->fan_duty_cycle[1])}, + {0x90, &(data->fan_speed[0])}, + {0x91, &(data->fan_speed[1])} + }; + + dev_dbg(&client->dev, "Starting cpr_4011_4mxx update\n"); + + /* Read byte data */ + for (i = 0; i < ARRAY_SIZE(regs_byte); i++) { + status = cpr_4011_4mxx_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; + } + } + + /* Read word data */ + for (i = 0; i < ARRAY_SIZE(regs_word); i++) { + status = cpr_4011_4mxx_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; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init cpr_4011_4mxx_init(void) +{ + return i2c_add_driver(&cpr_4011_4mxx_driver); +} + +static void __exit cpr_4011_4mxx_exit(void) +{ + i2c_del_driver(&cpr_4011_4mxx_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("CPR_4011_4MXX driver"); +MODULE_LICENSE("GPL"); + +module_init(cpr_4011_4mxx_init); +module_exit(cpr_4011_4mxx_exit); diff --git a/platform/broadcom/sonic-platform-modules-accton/common/modules/pmbus.h b/platform/broadcom/sonic-platform-modules-accton/common/modules/pmbus.h new file mode 100644 index 000000000000..cb6dbc34d87c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/common/modules/pmbus.h @@ -0,0 +1,389 @@ +/* + * pmbus.h - Common defines and structures for PMBus devices + * + * Copyright (c) 2010, 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PMBUS_H +#define PMBUS_H + +/* + * Registers + */ +#define PMBUS_PAGE 0x00 +#define PMBUS_OPERATION 0x01 +#define PMBUS_ON_OFF_CONFIG 0x02 +#define PMBUS_CLEAR_FAULTS 0x03 +#define PMBUS_PHASE 0x04 + +#define PMBUS_CAPABILITY 0x19 +#define PMBUS_QUERY 0x1A + +#define PMBUS_VOUT_MODE 0x20 +#define PMBUS_VOUT_COMMAND 0x21 +#define PMBUS_VOUT_TRIM 0x22 +#define PMBUS_VOUT_CAL_OFFSET 0x23 +#define PMBUS_VOUT_MAX 0x24 +#define PMBUS_VOUT_MARGIN_HIGH 0x25 +#define PMBUS_VOUT_MARGIN_LOW 0x26 +#define PMBUS_VOUT_TRANSITION_RATE 0x27 +#define PMBUS_VOUT_DROOP 0x28 +#define PMBUS_VOUT_SCALE_LOOP 0x29 +#define PMBUS_VOUT_SCALE_MONITOR 0x2A + +#define PMBUS_COEFFICIENTS 0x30 +#define PMBUS_POUT_MAX 0x31 + +#define PMBUS_FAN_CONFIG_12 0x3A +#define PMBUS_FAN_COMMAND_1 0x3B +#define PMBUS_FAN_COMMAND_2 0x3C +#define PMBUS_FAN_CONFIG_34 0x3D +#define PMBUS_FAN_COMMAND_3 0x3E +#define PMBUS_FAN_COMMAND_4 0x3F + +#define PMBUS_VOUT_OV_FAULT_LIMIT 0x40 +#define PMBUS_VOUT_OV_FAULT_RESPONSE 0x41 +#define PMBUS_VOUT_OV_WARN_LIMIT 0x42 +#define PMBUS_VOUT_UV_WARN_LIMIT 0x43 +#define PMBUS_VOUT_UV_FAULT_LIMIT 0x44 +#define PMBUS_VOUT_UV_FAULT_RESPONSE 0x45 +#define PMBUS_IOUT_OC_FAULT_LIMIT 0x46 +#define PMBUS_IOUT_OC_FAULT_RESPONSE 0x47 +#define PMBUS_IOUT_OC_LV_FAULT_LIMIT 0x48 +#define PMBUS_IOUT_OC_LV_FAULT_RESPONSE 0x49 +#define PMBUS_IOUT_OC_WARN_LIMIT 0x4A +#define PMBUS_IOUT_UC_FAULT_LIMIT 0x4B +#define PMBUS_IOUT_UC_FAULT_RESPONSE 0x4C + +#define PMBUS_OT_FAULT_LIMIT 0x4F +#define PMBUS_OT_FAULT_RESPONSE 0x50 +#define PMBUS_OT_WARN_LIMIT 0x51 +#define PMBUS_UT_WARN_LIMIT 0x52 +#define PMBUS_UT_FAULT_LIMIT 0x53 +#define PMBUS_UT_FAULT_RESPONSE 0x54 +#define PMBUS_VIN_OV_FAULT_LIMIT 0x55 +#define PMBUS_VIN_OV_FAULT_RESPONSE 0x56 +#define PMBUS_VIN_OV_WARN_LIMIT 0x57 +#define PMBUS_VIN_UV_WARN_LIMIT 0x58 +#define PMBUS_VIN_UV_FAULT_LIMIT 0x59 + +#define PMBUS_IIN_OC_FAULT_LIMIT 0x5B +#define PMBUS_IIN_OC_WARN_LIMIT 0x5D + +#define PMBUS_POUT_OP_FAULT_LIMIT 0x68 +#define PMBUS_POUT_OP_WARN_LIMIT 0x6A +#define PMBUS_PIN_OP_WARN_LIMIT 0x6B + +#define PMBUS_STATUS_BYTE 0x78 +#define PMBUS_STATUS_WORD 0x79 +#define PMBUS_STATUS_VOUT 0x7A +#define PMBUS_STATUS_IOUT 0x7B +#define PMBUS_STATUS_INPUT 0x7C +#define PMBUS_STATUS_TEMPERATURE 0x7D +#define PMBUS_STATUS_CML 0x7E +#define PMBUS_STATUS_OTHER 0x7F +#define PMBUS_STATUS_MFR_SPECIFIC 0x80 +#define PMBUS_STATUS_FAN_12 0x81 +#define PMBUS_STATUS_FAN_34 0x82 + +#define PMBUS_READ_VIN 0x88 +#define PMBUS_READ_IIN 0x89 +#define PMBUS_READ_VCAP 0x8A +#define PMBUS_READ_VOUT 0x8B +#define PMBUS_READ_IOUT 0x8C +#define PMBUS_READ_TEMPERATURE_1 0x8D +#define PMBUS_READ_TEMPERATURE_2 0x8E +#define PMBUS_READ_TEMPERATURE_3 0x8F +#define PMBUS_READ_FAN_SPEED_1 0x90 +#define PMBUS_READ_FAN_SPEED_2 0x91 +#define PMBUS_READ_FAN_SPEED_3 0x92 +#define PMBUS_READ_FAN_SPEED_4 0x93 +#define PMBUS_READ_DUTY_CYCLE 0x94 +#define PMBUS_READ_FREQUENCY 0x95 +#define PMBUS_READ_POUT 0x96 +#define PMBUS_READ_PIN 0x97 + +#define PMBUS_REVISION 0x98 +#define PMBUS_MFR_ID 0x99 +#define PMBUS_MFR_MODEL 0x9A +#define PMBUS_MFR_REVISION 0x9B +#define PMBUS_MFR_LOCATION 0x9C +#define PMBUS_MFR_DATE 0x9D +#define PMBUS_MFR_SERIAL 0x9E + +/* + * Virtual registers. + * Useful to support attributes which are not supported by standard PMBus + * registers but exist as manufacturer specific registers on individual chips. + * Must be mapped to real registers in device specific code. + * + * Semantics: + * Virtual registers are all word size. + * READ registers are read-only; writes are either ignored or return an error. + * RESET registers are read/write. Reading reset registers returns zero + * (used for detection), writing any value causes the associated history to be + * reset. + * Virtual registers have to be handled in device specific driver code. Chip + * driver code returns non-negative register values if a virtual register is + * supported, or a negative error code if not. The chip driver may return + * -ENODATA or any other error code in this case, though an error code other + * than -ENODATA is handled more efficiently and thus preferred. Either case, + * the calling PMBus core code will abort if the chip driver returns an error + * code when reading or writing virtual registers. + */ +#define PMBUS_VIRT_BASE 0x100 +#define PMBUS_VIRT_READ_TEMP_AVG (PMBUS_VIRT_BASE + 0) +#define PMBUS_VIRT_READ_TEMP_MIN (PMBUS_VIRT_BASE + 1) +#define PMBUS_VIRT_READ_TEMP_MAX (PMBUS_VIRT_BASE + 2) +#define PMBUS_VIRT_RESET_TEMP_HISTORY (PMBUS_VIRT_BASE + 3) +#define PMBUS_VIRT_READ_VIN_AVG (PMBUS_VIRT_BASE + 4) +#define PMBUS_VIRT_READ_VIN_MIN (PMBUS_VIRT_BASE + 5) +#define PMBUS_VIRT_READ_VIN_MAX (PMBUS_VIRT_BASE + 6) +#define PMBUS_VIRT_RESET_VIN_HISTORY (PMBUS_VIRT_BASE + 7) +#define PMBUS_VIRT_READ_IIN_AVG (PMBUS_VIRT_BASE + 8) +#define PMBUS_VIRT_READ_IIN_MIN (PMBUS_VIRT_BASE + 9) +#define PMBUS_VIRT_READ_IIN_MAX (PMBUS_VIRT_BASE + 10) +#define PMBUS_VIRT_RESET_IIN_HISTORY (PMBUS_VIRT_BASE + 11) +#define PMBUS_VIRT_READ_PIN_AVG (PMBUS_VIRT_BASE + 12) +#define PMBUS_VIRT_READ_PIN_MAX (PMBUS_VIRT_BASE + 13) +#define PMBUS_VIRT_RESET_PIN_HISTORY (PMBUS_VIRT_BASE + 14) +#define PMBUS_VIRT_READ_POUT_AVG (PMBUS_VIRT_BASE + 15) +#define PMBUS_VIRT_READ_POUT_MAX (PMBUS_VIRT_BASE + 16) +#define PMBUS_VIRT_RESET_POUT_HISTORY (PMBUS_VIRT_BASE + 17) +#define PMBUS_VIRT_READ_VOUT_AVG (PMBUS_VIRT_BASE + 18) +#define PMBUS_VIRT_READ_VOUT_MIN (PMBUS_VIRT_BASE + 19) +#define PMBUS_VIRT_READ_VOUT_MAX (PMBUS_VIRT_BASE + 20) +#define PMBUS_VIRT_RESET_VOUT_HISTORY (PMBUS_VIRT_BASE + 21) +#define PMBUS_VIRT_READ_IOUT_AVG (PMBUS_VIRT_BASE + 22) +#define PMBUS_VIRT_READ_IOUT_MIN (PMBUS_VIRT_BASE + 23) +#define PMBUS_VIRT_READ_IOUT_MAX (PMBUS_VIRT_BASE + 24) +#define PMBUS_VIRT_RESET_IOUT_HISTORY (PMBUS_VIRT_BASE + 25) +#define PMBUS_VIRT_READ_TEMP2_AVG (PMBUS_VIRT_BASE + 26) +#define PMBUS_VIRT_READ_TEMP2_MIN (PMBUS_VIRT_BASE + 27) +#define PMBUS_VIRT_READ_TEMP2_MAX (PMBUS_VIRT_BASE + 28) +#define PMBUS_VIRT_RESET_TEMP2_HISTORY (PMBUS_VIRT_BASE + 29) + +#define PMBUS_VIRT_READ_VMON (PMBUS_VIRT_BASE + 30) +#define PMBUS_VIRT_VMON_UV_WARN_LIMIT (PMBUS_VIRT_BASE + 31) +#define PMBUS_VIRT_VMON_OV_WARN_LIMIT (PMBUS_VIRT_BASE + 32) +#define PMBUS_VIRT_VMON_UV_FAULT_LIMIT (PMBUS_VIRT_BASE + 33) +#define PMBUS_VIRT_VMON_OV_FAULT_LIMIT (PMBUS_VIRT_BASE + 34) +#define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35) + +/* + * CAPABILITY + */ +#define PB_CAPABILITY_SMBALERT (1<<4) +#define PB_CAPABILITY_ERROR_CHECK (1<<7) + +/* + * VOUT_MODE + */ +#define PB_VOUT_MODE_MODE_MASK 0xe0 +#define PB_VOUT_MODE_PARAM_MASK 0x1f + +#define PB_VOUT_MODE_LINEAR 0x00 +#define PB_VOUT_MODE_VID 0x20 +#define PB_VOUT_MODE_DIRECT 0x40 + +/* + * Fan configuration + */ +#define PB_FAN_2_PULSE_MASK ((1 << 0) | (1 << 1)) +#define PB_FAN_2_RPM (1 << 2) +#define PB_FAN_2_INSTALLED (1 << 3) +#define PB_FAN_1_PULSE_MASK ((1 << 4) | (1 << 5)) +#define PB_FAN_1_RPM (1 << 6) +#define PB_FAN_1_INSTALLED (1 << 7) + +/* + * STATUS_BYTE, STATUS_WORD (lower) + */ +#define PB_STATUS_NONE_ABOVE (1<<0) +#define PB_STATUS_CML (1<<1) +#define PB_STATUS_TEMPERATURE (1<<2) +#define PB_STATUS_VIN_UV (1<<3) +#define PB_STATUS_IOUT_OC (1<<4) +#define PB_STATUS_VOUT_OV (1<<5) +#define PB_STATUS_OFF (1<<6) +#define PB_STATUS_BUSY (1<<7) + +/* + * STATUS_WORD (upper) + */ +#define PB_STATUS_UNKNOWN (1<<8) +#define PB_STATUS_OTHER (1<<9) +#define PB_STATUS_FANS (1<<10) +#define PB_STATUS_POWER_GOOD_N (1<<11) +#define PB_STATUS_WORD_MFR (1<<12) +#define PB_STATUS_INPUT (1<<13) +#define PB_STATUS_IOUT_POUT (1<<14) +#define PB_STATUS_VOUT (1<<15) + +/* + * STATUS_IOUT + */ +#define PB_POUT_OP_WARNING (1<<0) +#define PB_POUT_OP_FAULT (1<<1) +#define PB_POWER_LIMITING (1<<2) +#define PB_CURRENT_SHARE_FAULT (1<<3) +#define PB_IOUT_UC_FAULT (1<<4) +#define PB_IOUT_OC_WARNING (1<<5) +#define PB_IOUT_OC_LV_FAULT (1<<6) +#define PB_IOUT_OC_FAULT (1<<7) + +/* + * STATUS_VOUT, STATUS_INPUT + */ +#define PB_VOLTAGE_UV_FAULT (1<<4) +#define PB_VOLTAGE_UV_WARNING (1<<5) +#define PB_VOLTAGE_OV_WARNING (1<<6) +#define PB_VOLTAGE_OV_FAULT (1<<7) + +/* + * STATUS_INPUT + */ +#define PB_PIN_OP_WARNING (1<<0) +#define PB_IIN_OC_WARNING (1<<1) +#define PB_IIN_OC_FAULT (1<<2) + +/* + * STATUS_TEMPERATURE + */ +#define PB_TEMP_UT_FAULT (1<<4) +#define PB_TEMP_UT_WARNING (1<<5) +#define PB_TEMP_OT_WARNING (1<<6) +#define PB_TEMP_OT_FAULT (1<<7) + +/* + * STATUS_FAN + */ +#define PB_FAN_AIRFLOW_WARNING (1<<0) +#define PB_FAN_AIRFLOW_FAULT (1<<1) +#define PB_FAN_FAN2_SPEED_OVERRIDE (1<<2) +#define PB_FAN_FAN1_SPEED_OVERRIDE (1<<3) +#define PB_FAN_FAN2_WARNING (1<<4) +#define PB_FAN_FAN1_WARNING (1<<5) +#define PB_FAN_FAN2_FAULT (1<<6) +#define PB_FAN_FAN1_FAULT (1<<7) + +/* + * CML_FAULT_STATUS + */ +#define PB_CML_FAULT_OTHER_MEM_LOGIC (1<<0) +#define PB_CML_FAULT_OTHER_COMM (1<<1) +#define PB_CML_FAULT_PROCESSOR (1<<3) +#define PB_CML_FAULT_MEMORY (1<<4) +#define PB_CML_FAULT_PACKET_ERROR (1<<5) +#define PB_CML_FAULT_INVALID_DATA (1<<6) +#define PB_CML_FAULT_INVALID_COMMAND (1<<7) + +enum pmbus_sensor_classes { + PSC_VOLTAGE_IN = 0, + PSC_VOLTAGE_OUT, + PSC_CURRENT_IN, + PSC_CURRENT_OUT, + PSC_POWER, + PSC_TEMPERATURE, + PSC_FAN, + PSC_NUM_CLASSES /* Number of power sensor classes */ +}; + +#define PMBUS_PAGES 32 /* Per PMBus specification */ + +/* Functionality bit mask */ +#define PMBUS_HAVE_VIN (1 << 0) +#define PMBUS_HAVE_VCAP (1 << 1) +#define PMBUS_HAVE_VOUT (1 << 2) +#define PMBUS_HAVE_IIN (1 << 3) +#define PMBUS_HAVE_IOUT (1 << 4) +#define PMBUS_HAVE_PIN (1 << 5) +#define PMBUS_HAVE_POUT (1 << 6) +#define PMBUS_HAVE_FAN12 (1 << 7) +#define PMBUS_HAVE_FAN34 (1 << 8) +#define PMBUS_HAVE_TEMP (1 << 9) +#define PMBUS_HAVE_TEMP2 (1 << 10) +#define PMBUS_HAVE_TEMP3 (1 << 11) +#define PMBUS_HAVE_STATUS_VOUT (1 << 12) +#define PMBUS_HAVE_STATUS_IOUT (1 << 13) +#define PMBUS_HAVE_STATUS_INPUT (1 << 14) +#define PMBUS_HAVE_STATUS_TEMP (1 << 15) +#define PMBUS_HAVE_STATUS_FAN12 (1 << 16) +#define PMBUS_HAVE_STATUS_FAN34 (1 << 17) +#define PMBUS_HAVE_VMON (1 << 18) +#define PMBUS_HAVE_STATUS_VMON (1 << 19) + +enum pmbus_data_format { linear = 0, direct, vid }; + +struct pmbus_driver_info { + int pages; /* Total number of pages */ + enum pmbus_data_format format[PSC_NUM_CLASSES]; + /* + * Support one set of coefficients for each sensor type + * Used for chips providing data in direct mode. + */ + int m[PSC_NUM_CLASSES]; /* mantissa for direct data format */ + int b[PSC_NUM_CLASSES]; /* offset */ + int R[PSC_NUM_CLASSES]; /* exponent */ + + u32 func[PMBUS_PAGES]; /* Functionality, per page */ + /* + * The following functions map manufacturing specific register values + * to PMBus standard register values. Specify only if mapping is + * necessary. + * Functions return the register value (read) or zero (write) if + * successful. A return value of -ENODATA indicates that there is no + * manufacturer specific register, but that a standard PMBus register + * may exist. Any other negative return value indicates that the + * register does not exist, and that no attempt should be made to read + * the standard register. + */ + int (*read_byte_data)(struct i2c_client *client, int page, int reg); + int (*read_word_data)(struct i2c_client *client, int page, int reg); + int (*write_word_data)(struct i2c_client *client, int page, int reg, + u16 word); + int (*write_byte)(struct i2c_client *client, int page, u8 value); + /* + * The identify function determines supported PMBus functionality. + * This function is only necessary if a chip driver supports multiple + * chips, and the chip functionality is not pre-determined. + */ + int (*identify)(struct i2c_client *client, + struct pmbus_driver_info *info); + + int fan_num; +}; + +/* Function declarations */ + +void pmbus_clear_cache(struct i2c_client *client); +int pmbus_set_page(struct i2c_client *client, u8 page); +int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); +int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word); +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); +int pmbus_write_byte(struct i2c_client *client, int page, u8 value); +void _pmbus_clear_faults(struct i2c_client *client); +bool _pmbus_check_byte_register(struct i2c_client *client, int page, int reg); +bool _pmbus_check_word_register(struct i2c_client *client, int page, int reg); +int _pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, + struct pmbus_driver_info *info); +int pmbus_do_remove(struct i2c_client *client); +const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client + *client); + +#endif /* PMBUS_H */ diff --git a/platform/broadcom/sonic-platform-modules-accton/common/modules/ym2651y.c b/platform/broadcom/sonic-platform-modules-accton/common/modules/ym2651y.c new file mode 100644 index 000000000000..47e6a1d06bc5 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/common/modules/ym2651y.c @@ -0,0 +1,619 @@ +/* + * An hwmon driver for the 3Y Power YM-2651Y Power Module + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_FAN_DUTY_CYCLE 100 + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x58, 0x5b, I2C_CLIENT_END }; + +enum chips { + YM2651, + YM2401, + YM2851, +}; + +/* Each client has this additional data + */ +struct ym2651y_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 capability; /* Register value */ + u16 status_word; /* Register value */ + u8 fan_fault; /* Register value */ + u8 over_temp; /* Register value */ + u16 v_out; /* Register value */ + u16 i_out; /* Register value */ + u16 p_out; /* Register value */ + u16 temp; /* Register value */ + u16 fan_speed; /* Register value */ + u16 fan_duty_cycle[2]; /* Register value */ + u8 fan_dir[4]; /* Register value */ + u8 pmbus_revision; /* Register value */ + u8 mfr_id[10]; /* Register value */ + u8 mfr_model[10]; /* Register value */ + u8 mfr_revsion[3]; /* Register value */ + u16 mfr_vin_min; /* Register value */ + u16 mfr_vin_max; /* Register value */ + u16 mfr_iin_max; /* Register value */ + u16 mfr_iout_max; /* Register value */ + u16 mfr_pin_max; /* Register value */ + u16 mfr_pout_max; /* Register value */ + u16 mfr_vout_min; /* Register value */ + u16 mfr_vout_max; /* Register value */ +}; + +static ssize_t show_byte(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_word(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_linear(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_over_temp(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_ascii(struct device *dev, struct device_attribute *da, + char *buf); +static struct ym2651y_data *ym2651y_update_device(struct device *dev); +static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value); + +enum ym2651y_sysfs_attributes { + PSU_POWER_ON = 0, + PSU_TEMP_FAULT, + PSU_POWER_GOOD, + PSU_FAN1_FAULT, + PSU_FAN_DIRECTION, + PSU_OVER_TEMP, + PSU_V_OUT, + PSU_I_OUT, + PSU_P_OUT, + PSU_P_OUT_UV, /*In Unit of microVolt, instead of mini.*/ + PSU_TEMP1_INPUT, + PSU_FAN1_SPEED, + PSU_FAN1_DUTY_CYCLE, + PSU_PMBUS_REVISION, + PSU_MFR_ID, + PSU_MFR_MODEL, + PSU_MFR_REVISION, + PSU_MFR_VIN_MIN, + PSU_MFR_VIN_MAX, + PSU_MFR_VOUT_MIN, + PSU_MFR_VOUT_MAX, + PSU_MFR_IIN_MAX, + PSU_MFR_IOUT_MAX, + PSU_MFR_PIN_MAX, + PSU_MFR_POUT_MAX +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_power_on, S_IRUGO, show_word, NULL, PSU_POWER_ON); +static SENSOR_DEVICE_ATTR(psu_temp_fault, S_IRUGO, show_word, NULL, PSU_TEMP_FAULT); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_word, NULL, PSU_POWER_GOOD); +static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_fan_fault, NULL, PSU_FAN1_FAULT); +static SENSOR_DEVICE_ATTR(psu_over_temp, S_IRUGO, show_over_temp, NULL, PSU_OVER_TEMP); +static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, show_linear, NULL, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, show_linear, NULL, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, show_linear, NULL, PSU_P_OUT); +static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); +static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IWUSR | S_IRUGO, show_linear, set_fan_duty_cycle, PSU_FAN1_DUTY_CYCLE); +static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IRUGO, show_ascii, NULL, PSU_FAN_DIRECTION); +static SENSOR_DEVICE_ATTR(psu_pmbus_revision, S_IRUGO, show_byte, NULL, PSU_PMBUS_REVISION); +static SENSOR_DEVICE_ATTR(psu_mfr_id, S_IRUGO, show_ascii, NULL, PSU_MFR_ID); +static SENSOR_DEVICE_ATTR(psu_mfr_model, S_IRUGO, show_ascii, NULL, PSU_MFR_MODEL); +static SENSOR_DEVICE_ATTR(psu_mfr_revision, S_IRUGO, show_ascii, NULL, PSU_MFR_REVISION); +static SENSOR_DEVICE_ATTR(psu_mfr_vin_min, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MIN); +static SENSOR_DEVICE_ATTR(psu_mfr_vin_max, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_vout_min, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MIN); +static SENSOR_DEVICE_ATTR(psu_mfr_vout_max, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_iin_max, S_IRUGO, show_linear, NULL, PSU_MFR_IIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_iout_max, S_IRUGO, show_linear, NULL, PSU_MFR_IOUT_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_pin_max, S_IRUGO, show_linear, NULL, PSU_MFR_PIN_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_pout_max, S_IRUGO, show_linear, NULL, PSU_MFR_POUT_MAX); + +/*Duplicate nodes for lm-sensors.*/ +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_linear, NULL, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, show_linear, NULL, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, show_linear, NULL, PSU_P_OUT_UV); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); +static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_word, NULL, PSU_TEMP_FAULT); + +static struct attribute *ym2651y_attributes[] = { + &sensor_dev_attr_psu_power_on.dev_attr.attr, + &sensor_dev_attr_psu_temp_fault.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, + &sensor_dev_attr_psu_over_temp.dev_attr.attr, + &sensor_dev_attr_psu_v_out.dev_attr.attr, + &sensor_dev_attr_psu_i_out.dev_attr.attr, + &sensor_dev_attr_psu_p_out.dev_attr.attr, + &sensor_dev_attr_psu_temp1_input.dev_attr.attr, + &sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr, + &sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr, + &sensor_dev_attr_psu_fan_dir.dev_attr.attr, + &sensor_dev_attr_psu_pmbus_revision.dev_attr.attr, + &sensor_dev_attr_psu_mfr_id.dev_attr.attr, + &sensor_dev_attr_psu_mfr_model.dev_attr.attr, + &sensor_dev_attr_psu_mfr_revision.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vin_min.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_pout_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_iin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_pin_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vout_min.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vout_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_iout_max.dev_attr.attr, + /*Duplicate nodes for lm-sensors.*/ + &sensor_dev_attr_curr2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_power2_input.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + NULL +}; + +static ssize_t show_byte(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + + return (attr->index == PSU_PMBUS_REVISION) ? sprintf(buf, "%d\n", data->pmbus_revision) : + sprintf(buf, "0\n"); +} + +static ssize_t show_word(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + u16 status = 0; + + switch (attr->index) { + case PSU_POWER_ON: /* psu_power_on, low byte bit 6 of status_word, 0=>ON, 1=>OFF */ + status = (data->status_word & 0x40) ? 0 : 1; + break; + case PSU_TEMP_FAULT: /* psu_temp_fault, low byte bit 2 of status_word, 0=>Normal, 1=>temp fault */ + status = (data->status_word & 0x4) >> 2; + break; + case PSU_POWER_GOOD: /* psu_power_good, high byte bit 3 of status_word, 0=>OK, 1=>FAIL */ + status = (data->status_word & 0x800) ? 0 : 1; + break; + } + + return sprintf(buf, "%d\n", status); +} + +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(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct ym2651y_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; + + mutex_lock(&data->update_lock); + data->fan_duty_cycle[nr] = speed; + ym2651y_write_word(client, 0x3B + nr, data->fan_duty_cycle[nr]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_linear(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + + u16 value = 0; + int exponent, mantissa; + int multiplier = 1000; + + switch (attr->index) { + case PSU_V_OUT: + value = data->v_out; + break; + case PSU_I_OUT: + value = data->i_out; + break; + case PSU_P_OUT_UV: + multiplier = 1000000; /*For lm-sensors, unit is micro-Volt.*/ + /*Passing through*/ + case PSU_P_OUT: + value = data->p_out; + break; + case PSU_TEMP1_INPUT: + value = data->temp; + break; + case PSU_FAN1_SPEED: + value = data->fan_speed; + multiplier = 1; + break; + case PSU_FAN1_DUTY_CYCLE: + value = data->fan_duty_cycle[0]; + multiplier = 1; + break; + case PSU_MFR_VIN_MIN: + value = data->mfr_vin_min; + break; + case PSU_MFR_VIN_MAX: + value = data->mfr_vin_max; + break; + case PSU_MFR_VOUT_MIN: + value = data->mfr_vout_min; + break; + case PSU_MFR_VOUT_MAX: + value = data->mfr_vout_max; + break; + case PSU_MFR_PIN_MAX: + value = data->mfr_pin_max; + break; + case PSU_MFR_POUT_MAX: + value = data->mfr_pout_max; + break; + case PSU_MFR_IOUT_MAX: + value = data->mfr_iout_max; + break; + case PSU_MFR_IIN_MAX: + value = data->mfr_iin_max; + 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 show_fan_fault(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + + u8 shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6; + + return sprintf(buf, "%d\n", data->fan_fault >> shift); +} + +static ssize_t show_over_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct ym2651y_data *data = ym2651y_update_device(dev); + + return sprintf(buf, "%d\n", data->over_temp >> 7); +} + +static ssize_t show_ascii(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ym2651y_data *data = ym2651y_update_device(dev); + u8 *ptr = NULL; + + switch (attr->index) { + case PSU_FAN_DIRECTION: /* psu_fan_dir */ + ptr = data->fan_dir; + break; + case PSU_MFR_ID: /* psu_mfr_id */ + ptr = data->mfr_id; + break; + case PSU_MFR_MODEL: /* psu_mfr_model */ + ptr = data->mfr_model; + break; + case PSU_MFR_REVISION: /* psu_mfr_revision */ + ptr = data->mfr_revsion; + break; + default: + return 0; + } + + return sprintf(buf, "%s\n", ptr); +} + +static const struct attribute_group ym2651y_group = { + .attrs = ym2651y_attributes, +}; + +static int ym2651y_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct ym2651y_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct ym2651y_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &ym2651y_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &ym2651y_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int ym2651y_remove(struct i2c_client *client) +{ + struct ym2651y_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &ym2651y_group); + kfree(data); + + return 0; +} + +static const struct i2c_device_id ym2651y_id[] = { + { "ym2651", YM2651 }, + { "ym2401", YM2401 }, + { "ym2851", YM2851 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ym2651y_id); + +static struct i2c_driver ym2651y_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ym2651", + }, + .probe = ym2651y_probe, + .remove = ym2651y_remove, + .id_table = ym2651y_id, + .address_list = normal_i2c, +}; + +static int ym2651y_read_byte(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int ym2651y_read_word(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_data(client, reg); +} + +static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_word_data(client, reg, value); +} + +static int ym2651y_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 ym2651y_data *ym2651y_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ym2651y_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int i, status; + u8 command; + u8 fan_dir[5] = {0}; + struct reg_data_byte regs_byte[] = { {0x19, &data->capability}, + {0x7d, &data->over_temp}, + {0x81, &data->fan_fault}, + {0x98, &data->pmbus_revision} + }; + struct reg_data_word regs_word[] = { {0x79, &data->status_word}, + {0x8b, &data->v_out}, + {0x8c, &data->i_out}, + {0x96, &data->p_out}, + {0x8d, &data->temp}, + {0x3b, &(data->fan_duty_cycle[0])}, + {0x3c, &(data->fan_duty_cycle[1])}, + {0x90, &data->fan_speed}, + {0xa0, &data->mfr_vin_min}, + {0xa1, &data->mfr_vin_max}, + {0xa2, &data->mfr_iin_max}, + {0xa3, &data->mfr_pin_max}, + {0xa4, &data->mfr_vout_min}, + {0xa5, &data->mfr_vout_max}, + {0xa6, &data->mfr_iout_max}, + {0xa7, &data->mfr_pout_max} + }; + + dev_dbg(&client->dev, "Starting ym2651 update\n"); + + /* Read byte data */ + for (i = 0; i < ARRAY_SIZE(regs_byte); i++) { + status = ym2651y_read_byte(client, regs_byte[i].reg); + + if (status < 0) + { + dev_dbg(&client->dev, "reg %d, err %d\n", + regs_byte[i].reg, status); + *(regs_byte[i].value) = 0; + } + else { + *(regs_byte[i].value) = status; + } + } + + /* Read word data */ + for (i = 0; i < ARRAY_SIZE(regs_word); i++) { + status = ym2651y_read_word(client, regs_word[i].reg); + + if (status < 0) + { + dev_dbg(&client->dev, "reg %d, err %d\n", + regs_word[i].reg, status); + *(regs_word[i].value) = 0; + } + else { + *(regs_word[i].value) = status; + } + } + + /* Read fan_direction */ + command = 0xC3; + status = ym2651y_read_block(client, command, fan_dir, ARRAY_SIZE(fan_dir)-1); + + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + } + + strncpy(data->fan_dir, fan_dir+1, ARRAY_SIZE(data->fan_dir)-1); + data->fan_dir[ARRAY_SIZE(data->fan_dir)-1] = '\0'; + + /* Read mfr_id */ + command = 0x99; + status = ym2651y_read_block(client, command, data->mfr_id, + ARRAY_SIZE(data->mfr_id)-1); + data->mfr_id[ARRAY_SIZE(data->mfr_id)-1] = '\0'; + + if (status < 0) + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + + /* Read mfr_model */ + command = 0x9a; + status = ym2651y_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); + + /* Read mfr_revsion */ + command = 0x9b; + status = ym2651y_read_block(client, command, data->mfr_revsion, + ARRAY_SIZE(data->mfr_revsion)-1); + data->mfr_revsion[ARRAY_SIZE(data->mfr_revsion)-1] = '\0'; + + if (status < 0) + dev_dbg(&client->dev, "reg %d, err %d\n", command, status); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +module_i2c_driver(ym2651y_driver); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("3Y Power YM-2651Y driver"); +MODULE_LICENSE("GPL"); + + diff --git a/platform/broadcom/sonic-platform-modules-accton/debian/changelog b/platform/broadcom/sonic-platform-modules-accton/debian/changelog new file mode 100755 index 000000000000..2b08b12c206a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/debian/changelog @@ -0,0 +1,18 @@ +sonic-accton-platform-modules (1.1) unstable; urgency=low + + * Add support for AS7816-64X. + + -- Accton Network Tue, 19 Dec 2017 09:35:58 +0800 + +sonic-accton-platform-modules (1.1) unstable; urgency=low + + * Add support for Accton AS5712-54X + + -- Accton Network Wed, 11 Oct 2017 14:21:45 +0800 + +sonic-accton-platform-modules (1.0) unstable; urgency=low + + * Add support for Accton AS7712-32X + * Initial release + + -- Accton Network Thu, 01 Jun 2017 14:06:38 +0800 diff --git a/platform/broadcom/sonic-platform-modules-accton/debian/compat b/platform/broadcom/sonic-platform-modules-accton/debian/compat new file mode 100644 index 000000000000..ec635144f600 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/debian/compat @@ -0,0 +1 @@ +9 diff --git a/platform/broadcom/sonic-platform-modules-accton/debian/control b/platform/broadcom/sonic-platform-modules-accton/debian/control new file mode 100755 index 000000000000..22faf6d202b8 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/debian/control @@ -0,0 +1,41 @@ +Source: sonic-accton-platform-modules +Section: main +Priority: extra +Maintainer: Accton network , Accton Network +Build-Depends: debhelper (>= 8.0.0), bzip2 +Standards-Version: 3.9.3 + +Package: sonic-platform-accton-as7712-32x +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as fan, led, sfp + +Package: sonic-platform-accton-as5712-54x +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as fan, led, sfp + +Package: sonic-platform-accton-as7816-64x +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as fan, led, sfp + +Package: sonic-platform-accton-as7716-32x +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as fan, led, sfp + +Package: sonic-platform-accton-as7716-32xb +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as fan, led, sfp + +Package: sonic-platform-accton-as7312-54x +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as fan, led, sfp + +Package: sonic-platform-accton-as7326-56x +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as fan, led, sfp diff --git a/platform/broadcom/sonic-platform-modules-accton/debian/rules b/platform/broadcom/sonic-platform-modules-accton/debian/rules new file mode 100755 index 000000000000..8274607f27b5 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/debian/rules @@ -0,0 +1,86 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +include /usr/share/dpkg/pkg-info.mk + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +export INSTALL_MOD_DIR:=extra + +PYTHON ?= python2 + +PACKAGE_PRE_NAME := sonic-platform-accton +KVERSION ?= $(shell uname -r) +KERNEL_SRC := /lib/modules/$(KVERSION) +MOD_SRC_DIR:= $(shell pwd) +MODULE_DIRS:= as7712-32x as5712-54x as7816-64x as7716-32x as7716-32xb as7312-54x as7326-56x +MODULE_DIR := modules +UTILS_DIR := utils +SERVICE_DIR := service +CLASSES_DIR := classes +CONF_DIR := conf + +%: + dh $@ --with systemd,python2,python3 --buildsystem=pybuild + +clean: + dh_testdir + dh_testroot + dh_clean + +build: + #make modules -C $(KERNEL_SRC)/build M=$(MODULE_SRC) + (for mod in $(MODULE_DIRS); do \ + make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + $(PYTHON) $${mod}/setup.py build; \ + done) + +binary: binary-arch binary-indep + # Nothing to do + +binary-arch: + # Nothing to do + +#install: build + #dh_testdir + #dh_testroot + #dh_clean -k + #dh_installdirs + +binary-indep: + dh_testdir + dh_installdirs + + # Custom package commands + (for mod in $(MODULE_DIRS); do \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin; \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system; \ + cp $(MOD_SRC_DIR)/$${mod}/$(MODULE_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$${mod}/$(UTILS_DIR)/* debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin/; \ + cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/*.service debian/$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system/; \ + $(PYTHON) $${mod}/setup.py install --root=$(MOD_SRC_DIR)/debian/$(PACKAGE_PRE_NAME)-$${mod} --install-layout=deb; \ + done) + # Resuming debhelper scripts + dh_testroot + dh_install + dh_installchangelogs + dh_installdocs + dh_systemd_enable + dh_installinit + dh_systemd_start + dh_link + dh_fixperms + dh_compress + dh_strip + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb +.PHONY: build binary binary-arch binary-indep clean diff --git a/platform/broadcom/sonic-platform-modules-arista b/platform/broadcom/sonic-platform-modules-arista deleted file mode 160000 index a35c7eddaf94..000000000000 --- a/platform/broadcom/sonic-platform-modules-arista +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a35c7eddaf945a1ff09ea71b47f69b6ddba5e892 diff --git a/platform/broadcom/sonic-platform-modules-arista/.gitignore b/platform/broadcom/sonic-platform-modules-arista/.gitignore new file mode 100644 index 000000000000..72220960de3f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/.gitignore @@ -0,0 +1,21 @@ +# kernel module build +*.o +*.ko +*.mod.c +*.mod.o +*.cmd +Module.symvers +modules.order + +# debian packaging +*.debhelper +*.log +DEBIAN +.tmp_versions +.finished.build + +# python +*.pyc +/build +/*.egg-info +/venv diff --git a/platform/broadcom/sonic-platform-modules-arista/LICENSE b/platform/broadcom/sonic-platform-modules-arista/LICENSE new file mode 100644 index 000000000000..ebdb940a0c98 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/LICENSE @@ -0,0 +1,1016 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + + + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/platform/broadcom/sonic-platform-modules-arista/Makefile b/platform/broadcom/sonic-platform-modules-arista/Makefile new file mode 100644 index 000000000000..ff463b6e013f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/Makefile @@ -0,0 +1,17 @@ +SHELL = /bin/bash +.ONESHELL: +.SHELLFLAGS += -e + +MAIN_TARGET = $(ARISTA_PLATFORM_MODULE) +EXTRA_TARGETS = $(ARISTA_PLATFORM_MODULE_DRIVERS) \ + $(ARISTA_PLATFORM_MODULE_PYTHON2) \ + $(ARISTA_PLATFORM_MODULE_PYTHON3) + +$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : + # Build the package + export PYBUILD_INSTALL_ARGS_python2=--install-scripts=/dev/null + dpkg-buildpackage -rfakeroot -b -us -uc -j$(SONIC_CONFIG_MAKE_JOBS) + + mv $(addprefix ../, $* $(EXTRA_TARGETS)) $(DEST)/ + +$(addprefix $(DEST)/, $(EXTRA_TARGETS)): $(DEST)/% : $(DEST)/$(MAIN_TARGET) diff --git a/platform/broadcom/sonic-platform-modules-arista/README.md b/platform/broadcom/sonic-platform-modules-arista/README.md new file mode 100644 index 000000000000..16f1da548dad --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/README.md @@ -0,0 +1,250 @@ +Arista platform support for SONiC +================================= + +Copyright (C) 2016 Arista Networks, Inc. + +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 3 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, see . + +## License + +All linux kernel code is licensed under the GPLv2. All other code is +licensed under the GPLv3. Please see the LICENSE file for copies of +both licenses. + +## Purpose + +This package contains kernel drivers, a python library and a python binary to +provide platform support for Arista switches. + +The `arista` tool will identify the platform on which it is running and will +then load and initialise the required kernel drivers. Once initialised, the +system will expose transceivers, leds, fans, temperature sensors and gpios +through the sysfs. + +## Supported platforms + +The following platforms are currently supported, + + - DCS-7050QX-32 + - DCS-7050QX-32S + - DCS-7060CX-32 + - DCS-7260CX3-64 + +Some platforms will require some custom kernel patches. +They are available on the Azure/sonic-linux-kernel repository. + +## Usage + +This repository contains both a `systemd` and `LSB` init script to properly +integrate with the system startup and shutdown. + +Alternatively the `arista` tool can be used in standalone to load and unload +the platform support. + +``` + arista --help +``` + +Since the python library knows about the current platform, it can provides a +common and unified implementation of the SONiC plugins. +Currently supports `eeprom`, `sfputil` and `led_control`. + +The python library and tools are python2 and python3 compatible. + +## Drivers + +The kernel drivers in this repository have currently been tested against a +3.16 and 3.18 based kernel image. + +### scd-hwmon vs sonic-support-driver + +The `scd-hwmon` is the newer implementation of the scd driver and is used +for all supported platforms except `DCS-7050QX-32` and `DCS-7050QX-32S` which +use `sonic-support driver`. + +When the `scd-hwmon` driver is loaded, the various gpios and resets can be set +and unset by writing into the sysfs file. +The meaning of the value `0` or `1` read from or written to is determined by +the name of the sysfs entry. + +``` +cd /sys/module/scd/drivers/pci:scd// +echo 1 > switch_chip_reset +``` + +When the legacy `sonic-support-driver` is in use, the gpios and resets behave +according to the gpio subsystem of the kernel. The driver will properly set +`value` and `active_low`, whereas `direction` must be set to `out` when +setting the gpio and to `in` when reading it. Read only entries don't have +a `direction` file. + +``` +cd /sys/module/scd/drivers/pci:scd// +echo out > switch_chip_reset/direction +echo 1 > switch_chip_reset/value +echo 0 > switch_chip_reset/value +``` + +## Components + +This section describes how to interact with the various components exposed by +the kernel drivers. +In order to see them, the platform must be successfully initialized. + +The following sections describe how to manually interact with the components. +Examples shown may differ across platforms but the logic stays the same. + +### LEDs + +LED entries can be found under `/sys/class/leds`. Since the sysfs interface +for LEDs is not very expressive, the brightness field is used here +to toggle between off and different colors. The brightness to LED +color mappings are as follows (0 maps to off for all LEDs): + +``` +status, fan_status, psu1, psu2: + 0 => off + 1 => green + 2 => red + +beacon: + 1+ => blue + +qsfp: + 1 => green + 2 => yellow + +fan: + 1 => green + 2 => red + 3 => yellow +``` + +Given that this repository is primarily aimed at running on SONiC, an +implementation of the `led_control` plugin is available under +`arista.utils.sonic_leds`. It requires access to the `port_config.ini` file to +translate from `interface name` to `front panel port`. + +### Fans + +Fans are exposed under `/sys/class/hwmon/*` and respect the +[sysfs-interface](https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface) +guidelines. + +This repository provides the kernel modules to handle the fans. + +### Temperature sensors + +Temperature sensors are exposed under `/sys/class/hwmon/*` and also respect +the [sysfs-interface](https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface). + +They are all managed by linux standard modules like `lm73` and `max6658`. + +### Power supplies + +Power supplies and power controllers can be managed by the kernel's +generic `pmbus` module. Assuming the pmbus module was compiled into the +kernel. + +Some power supplies may need kernel patches against the `pmbus` driver. + +### System EEPROM + +The system eeprom contains platform specific information like the `SKU`, the +`serial number` and the `base mac address`. + +The way to read the system eeprom from a platform can differ from one SKU to the +other. +The most reliable way to get this information is by issuing `arista syseeprom` + +In the case of SONiC the module `arista.utils.sonic_eeprom` provide the plugin +implementation. + +### Transceivers - QSFPs / SFPs + +Currently only platforms with QSFP+ and SFP+ ports are supported. +These transceivers provide 2 kind of information. + +#### Pins + +The first piece of information is obtained from the transceiver physical pins. + - QSFP: present, reset, low power mode, interrupt, module select + - SFP: present, rxlos, txfault, txdisable + +These knobs are accessible under `/sys/module/scd/drivers/pci:scd/.../` +The name of the entries follow this naming `_` +For example `qsfp2_reset` or `sfp66_txdisable`. + +See [this section](#scd-hwmon-vs-sonic-support-driver) on how to use them. + +#### Eeproms + +The second piece of information provided by a transceiver is the content of its +`eeprom`. It is accessible via `SMBus` at the fixed address `0x50`. + +On linux, an unoffical module called `sff_8436_eeprom` can handle such devices. +The arista initialisation library takes care of loading the module for all the +transceivers. +They should then all be available under +`/sys/module/sff_8436_eeprom/drivers/i2c:sff8436` + +After instantiation, the EEPROM information can be read like so: + +``` +root@sonic# hexdump -C /sys/bus/i2c/devices/19-0050/eeprom +00000000 0d 00 02 f0 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000010 00 00 00 00 00 00 19 5c 00 00 7f 9c 00 00 00 00 |.......\........| +00000020 00 00 1f cd 20 2e 26 b8 22 94 00 00 00 00 00 00 |.... .&.".......| +00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00000070 00 00 00 00 00 04 02 00 00 00 00 00 00 00 00 00 |................| +00000080 0d 00 0c 04 00 00 00 00 00 00 00 05 67 00 00 32 |............g..2| +00000090 00 00 00 00 41 72 69 73 74 61 20 4e 65 74 77 6f |....Arista Netwo| +000000a0 72 6b 73 20 00 00 1c 73 51 53 46 50 2d 34 30 47 |rks ...sQSFP-40G| +000000b0 2d 53 52 34 20 20 20 20 30 33 42 68 07 d0 46 0d |-SR4 03Bh..F.| +000000c0 00 00 0f de 58 4d 44 31 34 30 34 30 30 35 56 52 |....XMD1404005VR| +000000d0 20 20 20 20 31 34 30 31 32 36 20 20 08 00 00 d2 | 140126 ....| +000000e0 10 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000f0 00 00 00 00 00 00 02 f8 00 00 00 00 98 44 64 d1 |.............Dd.| +00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00000180 40 6b a1 2e 3c f4 2b eb cd c1 e9 51 50 93 bb fe |@k..<.+....QP...| +00000190 05 aa 32 3f 1c 4c 00 00 00 00 00 00 00 00 00 00 |..2?.L..........| +000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00000200 4b 00 fb 00 46 00 00 00 00 00 00 00 00 00 00 00 |K...F...........| +00000210 8d cc 74 04 87 5a 7a 75 00 00 00 00 00 00 00 00 |..t..Zzu........| +00000220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000230 55 76 01 be 43 e2 04 62 13 88 00 fa 12 8e 01 f4 |Uv..C..b........| +00000240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00000260 00 00 00 00 00 00 00 00 00 00 00 00 33 33 77 77 |............33ww| +00000270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000280 +``` + +Before being read, the QSFP+ modules must be taken out of reset and +have their module select signals asserted. This can be done through +the GPIO interface. + +### QSFP - SFP multiplexing + +On the `DCS-7050QX-32S`, the first QSFP port and the 4 SFP ports are multiplexed. +To choose between one or the other, write into the sysfs file located under +`/sys/modules/scd/drivers/pci:scd/.../mux_sfp_qsfp` + +### GPIOs and resets + +Most of the GPIOs are exposed by the `scd-hwmon` and the `sonic-support-driver`. +They should be available under `/sys/module/scd/drivers/pci:scd/.../`. diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/__init__.py b/platform/broadcom/sonic-platform-modules-arista/arista/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/components/__init__.py b/platform/broadcom/sonic-platform-modules-arista/arista/components/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/components/common.py b/platform/broadcom/sonic-platform-modules-arista/arista/components/common.py new file mode 100644 index 000000000000..d925dab7fe98 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/components/common.py @@ -0,0 +1,109 @@ + +import logging +import os +import time + +from ..core.component import Component, DEFAULT_WAIT_TIMEOUT, ASIC_YIELD_TIME +from ..core.driver import KernelDriver +from ..core.utils import klog, inSimulation +from ..core.types import PciAddr, I2cAddr + +class PciComponent(Component): + def __init__(self, addr, **kwargs): + assert isinstance(addr, PciAddr) + super(PciComponent, self).__init__(addr=addr, **kwargs) + +class I2cComponent(Component): + def __init__(self, addr, **kwargs): + assert isinstance(addr, I2cAddr) + super(I2cComponent, self).__init__(addr=addr, **kwargs) + +class I2cKernelComponent(I2cComponent): + def __init__(self, addr, name, waitFile=None, **kwargs): + super(I2cKernelComponent, self).__init__(addr, **kwargs) + self.addDriver(I2cKernelDriver, name, waitFile) + +class PciKernelDriver(KernelDriver): + def __init__(self, component, name, args=None): + assert isinstance(component, PciComponent) + super(PciKernelDriver, self).__init__(component, name, args) + + def getSysfsPath(self): + return os.path.join('/sys/bus/pci/devices', str(self.component.addr)) + +class I2cKernelDriver(KernelDriver): + def __init__(self, component, name, waitFile=None): + assert isinstance(component, I2cComponent) + super(I2cKernelDriver, self).__init__(component, None, waitFile) + self.name = name + + def getSysfsPath(self): + return os.path.join('/sys/bus/i2c/devices', str(self.component.addr)) + + def getSysfsBusPath(self): + return '/sys/bus/i2c/devices/i2c-%d' % self.component.addr.bus + + def setup(self): + addr = self.component.addr + devicePath = self.getSysfsPath() + path = os.path.join(self.getSysfsBusPath(), 'new_device') + logging.debug('creating i2c device %s on bus %d at 0x%02x', + self.name, addr.bus, addr.address) + if inSimulation(): + return + if os.path.exists(devicePath): + logging.debug('i2c device %s already exists', devicePath) + else: + with open(path, 'w') as f: + f.write('%s 0x%02x' % (self.name, self.component.addr.address)) + self.waitFileReady() + + def clean(self): + # i2c kernel devices are automatically cleaned when the module is removed + if inSimulation(): + return + path = os.path.join(self.getSysfsBusPath(), 'delete_device') + addr = self.component.addr + if os.path.exists(self.getSysfsPath()): + logging.debug('removing i2c device %s from bus %d' % (self.name, addr.bus)) + with open(path, 'w') as f: + f.write('0x%02x' % addr.address) + + def __str__(self): + return '%s(%s)' % (self.__class__.__name__, self.name) + +class SwitchChip(PciComponent): + def pciRescan(self): + logging.info('triggering kernel pci rescan') + with open('/sys/bus/pci/rescan', 'w') as f: + f.write('1\n') + + def waitForIt(self, timeout=DEFAULT_WAIT_TIMEOUT): + begin = time.time() + end = begin + timeout + rescanTime = begin + (timeout / 2) + devPath = os.path.join('/sys/bus/pci/devices/', str(self.addr)) + + logging.debug('waiting for switch chip %s', devPath) + if inSimulation(): + return True + + klog('waiting for switch chip') + while True: + now = time.time() + if now > end: + break + if os.path.exists(devPath): + logging.debug('switch chip is ready') + klog('switch chip is ready') + time.sleep(ASIC_YIELD_TIME) + klog('yielding...') + return True + if now > rescanTime: + self.pciRescan() + rescanTime = end + time.sleep(0.1) + + logging.error('timed out waiting for the switch chip %s', devPath) + return False + diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/components/ds125br.py b/platform/broadcom/sonic-platform-modules-arista/arista/components/ds125br.py new file mode 100644 index 000000000000..22f773a39d55 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/components/ds125br.py @@ -0,0 +1,84 @@ +import logging +import copy + +from ..core.utils import SMBus +from .common import I2cComponent + +class Ds125Br(I2cComponent): + def __init__(self, addr, **kwargs): + super(Ds125Br, self).__init__(addr, channels=8, **kwargs) + + def qsfpPortConfig(self, amplitude): + disableCrc = 0x18 + squelchMode = 0x40 + outputAmplitude = amplitude + assert len(outputAmplitude) == self.channels + txDeEmphasis = [0x00] * self.channels + rxEqualization = [0x00] * self.channels + inputTermination = [0x0c] * self.channels + squelch = [0x02] * self.channels + return [disableCrc, squelchMode, outputAmplitude, txDeEmphasis, + rxEqualization, inputTermination, squelch] + + def qsfpPortGroupConfig(self): + portConfig = self.qsfpPortConfig([None, 0xac, None, 0xac, + 0xab, 0xab, 0xab, 0xac]) + txDeEmphasis = portConfig[3] + txDeEmphasis[0] = None + txDeEmphasis[2] = None + portConfig[3] = txDeEmphasis + return portConfig + + def getPortConfigs(self): + qsfp35 = ( + 0x59, + self.qsfpPortConfig([0xaa, 0xaa, 0xaa, 0xaa, 0xa8, 0xa9, 0xa8, 0xa9]) + ) + qsfp36 = ( + 0x58, + self.qsfpPortConfig([0xaa, 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xaa]) + ) + sfp1_2 = ( + 0x5a, + self.qsfpPortGroupConfig() + ) + sfp3_4 = ( + 0x5b, + self.qsfpPortGroupConfig() + ) + return [qsfp35, qsfp36, sfp1_2, sfp3_4] + + def setupPort(self, bus, addr, config): + disableCrc, squelchMode, outputAmplitude, txDeEmphasis, \ + rxEqualization, inputTermination, squelch = config + + bus.write_byte_data(addr, 0x06, disableCrc) + bus.write_byte_data(addr, 0x28, squelchMode) + + baseAddr = 0x0d + for channel in range(0, self.channels): + offset = channel * 7 + if (baseAddr + offset) > 0x27: + offset += 1 + regs = [ + (baseAddr + offset + 0, squelch[channel]), + (baseAddr + offset + 1, inputTermination[channel]), + (baseAddr + offset + 2, rxEqualization[channel]), + (baseAddr + offset + 3, outputAmplitude[channel]), + (baseAddr + offset + 4, txDeEmphasis[channel]), + ] + for reg, data in regs: + if data is not None: + logging.debug('i2c-write %#02x %#02x %#02x', addr, reg, data) + bus.write_byte_data(addr, reg, data) + + def setup(self): + logging.debug('setting up ds125br repeaters') + + bus = SMBus(self.addr.bus) + for addr, config in self.getPortConfigs(): + self.setupPort(bus, addr, config) + + def clean(self): + pass + diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/components/ds460.py b/platform/broadcom/sonic-platform-modules-arista/arista/components/ds460.py new file mode 100644 index 000000000000..ce72beb20a7b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/components/ds460.py @@ -0,0 +1,37 @@ +import logging + +from ..core.utils import SMBus + +from .common import I2cKernelComponent + +class Ds460(I2cKernelComponent): + def __init__(self, addr, **kwargs): + # pmbus if dps460 is not available + super(Ds460, self).__init__(addr, 'dps460', **kwargs) + + def setup(self): + addr = self.addr.address + + logging.debug('initializing ds460 registers') + bus = SMBus(self.addr.bus) + + try: + bus.read_byte_data(addr, 0x00) + except IOError: + logging.debug('device ds460 not found') + bus.close() + return + + try: + byte = bus.read_byte_data(addr, 0x10) + bus.write_byte_data(addr, 0x10, 0) + bus.write_byte_data(addr, 0x03, 1) + bus.write_byte_data(addr, 0x10, byte) + except IOError: + logging.debug('failed to initialize ds460') + + bus.close() + + super(Ds460, self).setup() + + diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/components/scd.py b/platform/broadcom/sonic-platform-modules-arista/arista/components/scd.py new file mode 100644 index 000000000000..ac610068a3a5 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/components/scd.py @@ -0,0 +1,473 @@ +from __future__ import print_function, with_statement + +import os +import logging + +from collections import OrderedDict, namedtuple + +from ..core.inventory import Xcvr, Psu +from ..core.types import Gpio, ResetGpio, NamedGpio +from ..core.utils import sysfsFmtHex, sysfsFmtDec, sysfsFmtStr, simulateWith, \ + inSimulation + +from .common import PciComponent, KernelDriver, PciKernelDriver + +class ScdSysfsGroup(object): + def __init__(self, objNum, typeStr, driver): + self.driver = driver + self.prefix = '%s%d_' % (typeStr, objNum) + self.prefixPath = os.path.join(driver.getSysfsPath(), self.prefix) + +class ScdSysfsRW(ScdSysfsGroup): + def __init__(self, objNum, typeStr, driver): + ScdSysfsGroup.__init__(self, objNum, typeStr, driver) + + def getSysfsGpio(self, name): + return '%s%s' % (self.prefixPath, name) + + def readValueSim(self, name): + logging.info('read sysfs %s entry %s', self.prefix, name) + return "1" + + @simulateWith(readValueSim) + def readValue(self, name): + with open(self.getSysfsGpio(name), 'r') as f: + return f.read().rstrip() + + def writeValueSim(self, name, value): + logging.info('write sysfs %s entry %s value %s', self.prefix, name, value) + return True + + @simulateWith(writeValueSim) + def writeValue(self, name, value): + with open(self.getSysfsGpio(name), 'w') as f: + f.write(str(value)) + return True + +class ScdSysfsOldRW(ScdSysfsRW): + def __init__(self, objNum, typeStr, driver): + ScdSysfsRW.__init__(self, objNum, typeStr, driver) + + def readValue(self, name): + gpio = self.getSysfsGpio(name) + directionPath = os.path.join(gpio, 'direction') + if os.path.exists(directionPath): + with open(directionPath, 'w') as f: + f.write("in") + with open(os.path.join(gpio, 'value'), 'r') as f: + return f.read().rstrip() + + def writeValue(self, name, value): + gpio = self.getSysfsGpio(name) + directionPath = os.path.join(gpio, 'direction') + if os.path.exists(directionPath): + with open(directionPath, 'w') as f: + f.write("out") + with open(os.path.join(gpio, 'value'), 'w') as f: + f.write(str(value)) + return True + +class ScdKernelXcvr(Xcvr): + def __init__(self, portNum, xcvrType, eepromAddr, bus, driver, sysfsRWClass): + Xcvr.__init__(self, portNum, xcvrType, eepromAddr, bus) + typeStr = 'qsfp' if xcvrType == Xcvr.QSFP else 'sfp' + self.rw = sysfsRWClass(portNum, typeStr, driver) + + def getPresence(self): + return self.rw.readValue('present') == '1' + + def getLowPowerMode(self): + if self.xcvrType == Xcvr.SFP: + return False + return self.rw.readValue('lp_mode') == '1' + + def setLowPowerMode(self, value): + if self.xcvrType == Xcvr.SFP: + return False + return self.rw.writeValue('lp_mode', '1' if value else '0') + + def reset(self, value): + if self.xcvrType == Xcvr.SFP: + return False + return self.rw.writeValue('reset', '1' if value else '0') + +class ScdKernelPsu(Psu): + def __init__(self, portNum, driver, sysfsRWClass, statusSupported): + self.rw = sysfsRWClass(portNum, 'psu', driver) + self.statusSupported = statusSupported + + def getPresence(self): + return self.rw.readValue('present') == '1' + + def getStatus(self): + if self.statusSupported: + return self.rw.readValue('status') == '1' + else: + return self.getPresence() + +class ScdHwmonKernelDriver(PciKernelDriver): + def __init__(self, scd): + super(ScdHwmonKernelDriver, self).__init__(scd, 'scd-hwmon') + + def writeConfigSim(self, path, data): + for filename, value in data.items(): + logging.info('writting data under %s : %r', + os.path.join(path, filename), value) + + @simulateWith(writeConfigSim) + def writeConfig(self, path, data): + for filename, value in data.items(): + try: + path = os.path.join(path, filename) + with open(path, 'w') as f: + f.write(value) + except IOError as e: + logging.error('%s %s', path, e.strerror) + + def writeComponents(self, components, filename): + PAGE_SIZE = 4096 + data = [] + data_size = 0 + + for entry in components: + entry_size = len(entry) + 1 + if entry_size + data_size > PAGE_SIZE: + self.writeConfig(self.getSysfsPath(), {filename: '\n'.join(data)}) + data_size = 0 + data = [] + data.append(entry) + data_size += entry_size + + if data: + self.writeConfig(self.getSysfsPath(), {filename: '\n'.join(data)}) + + def setup(self): + super(ScdHwmonKernelDriver, self).setup() + + scd = self.component + data = [] + + for addr, info in scd.masters.items(): + data += ["master %#x %d %d" % (addr, info['id'], info['bus'])] + + for addr, name in scd.leds: + data += ["led %#x %s" % (addr, name)] + + for addr, info in scd.qsfps.items(): + data += ["qsfp %#x %u" % (addr, info['id'])] + + for addr, info in scd.sfps.items(): + data += ["sfp %#x %u" % (addr, info['id'])] + + for reset in scd.resets: + data += ["reset %#x %s %u" % (reset.addr, reset.name, reset.bit)] + + for gpio in scd.gpios: + data += ["gpio %#x %s %u %d %d" % (gpio.addr, gpio.name, gpio.bit, + int(gpio.ro), int(gpio.activeLow))] + + tweaks = [] + for tweak in scd.tweaks: + tweaks += ["%#x %#x %#x %#x %#x" % ( + tweak.bus, tweak.addr, tweak.t, tweak.datr, tweak.datw)] + + logging.debug('creating scd objects') + self.writeComponents(data, "new_object") + + if tweaks: + logging.debug('applying scd tweaks') + self.writeComponents(tweaks, "smbus_tweaks") + + def finish(self): + logging.debug('applying scd configuration') + path = self.getSysfsPath() + self.writeConfig(path, {'init_trigger': '1'}) + super(ScdHwmonKernelDriver, self).finish() + + def resetSim(self, value): + resets = self.component.getSysfsResetNameList() + logging.debug('reseting devices %s', resets) + + @simulateWith(resetSim) + def reset(self, value): + path = self.getSysfsPath() + for reset in self.component.getSysfsResetNameList(): + with open(os.path.join(path, reset), 'w') as f: + f.write('1' if value else '0') + + def resetIn(self): + self.reset(True) + + def resetOut(self): + self.reset(False) + +class ScdKernelDriver(PciKernelDriver): + def __init__(self, scd): + super(ScdKernelDriver, self).__init__(scd, 'sonic-support-driver') + + def oldSetup(self): + # converting internals to old format + scd = self.component + + qsfpType = 0 + sfpType = 1 + psuType = 2 + muxType = 3 + + resets = {} + for reset in scd.resets: + assert isinstance(reset, ResetGpio), "Invalid type for reset %s" % reset + v = resets.setdefault(reset.addr, []) + v.append((reset.bit, reset.name)) + + gpios = OrderedDict() + gpio_names = [] + gpio_type = [] + for gpio in scd.gpios: + assert isinstance(gpio, NamedGpio), "Invalid type for gpio %s" % gpio + v = gpios.setdefault(gpio.addr, []) + v.append(Gpio(gpio.bit, gpio.ro, gpio.activeLow)) + + # FIXME: works since only psus are gpios and the driver behave strangely + gpio_type.append(psuType) + gpio_names.append("psu") + if len(scd.gpios) > 2: + gpio_type.append(muxType) + gpio_names.append("mux") + + for addr, data in scd.qsfps.items(): + gpio_names.append("qsfp%d" % data['id']) + gpio_type.append(qsfpType) + gpios[addr] = data['gpios'] + + for addr, data in scd.sfps.items(): + gpio_names.append("sfp%d" % data['id']) + gpio_type.append(sfpType) + gpios[addr] = data['gpios'] + + # generating values + + reset_addrs = sorted(resets) + reset_names = [] + reset_masks = [] + + for addr in reset_addrs: + mask = 0 + (bits, names) = zip(*resets[addr]) + reset_names.extend(names) + for bit in bits: + mask |= (1 << bit) + reset_masks.append(mask) + + gpio_addrs = gpios.keys() + gpio_masks = [] + gpio_ro = [] + gpio_active_low = [] + + for addr in gpio_addrs: + mask = 0 + ro_mask = 0 + active_low_mask = 0 + for (bit, ro, active_low) in gpios[addr]: + mask |= (1 << bit) + if ro: + ro_mask |= (1 << bit) + if active_low: + active_low_mask |= (1 << bit) + gpio_masks.append(mask) + gpio_ro.append(ro_mask) + gpio_active_low.append(active_low_mask) + + (led_addrs, led_names) = zip(*scd.leds) + master_addrs = scd.masters.keys() + + files = [ + ('master_addrs', sysfsFmtHex), + ('reset_addrs', sysfsFmtHex), + ('reset_names', sysfsFmtStr), + ('reset_masks', sysfsFmtHex), + ('gpio_addrs', sysfsFmtHex), + ('gpio_masks', sysfsFmtHex), + ('gpio_names', sysfsFmtStr), + ('gpio_ro', sysfsFmtDec), + ('gpio_type', sysfsFmtHex), + ('gpio_active_low', sysfsFmtDec), + ('led_addrs', sysfsFmtHex), + ('led_names', sysfsFmtStr) + ] + variables = locals() + return {key: ",".join(map(fmt, variables[key])) for key, fmt in files} + + def writeConfig(self, path, data): + if inSimulation(): + print(data) + else: + for filename, value in data.items(): + try: + with open(os.path.join(path, filename), 'w') as f: + f.write(value) + except IOError as e: + logging.error('%s %s' % (e.filename, e.strerror)) + + def getConfigSysfsPath(self): + return os.path.join(self.getSysfsPath(), 'sonic_support_driver', + 'sonic_support_driver') + def setup(self): + super(ScdKernelDriver, self).setup() + data = self.oldSetup() + self.writeConfig(self.getConfigSysfsPath(), data) + + def finish(self): + logging.debug('applying scd configuration') + path = self.getSysfsPath() + self.writeConfig(path, {'init_trigger': '1'}) + + # FIXME: the direction should be set properly by the driver + logging.debug('setting gpio directions') + data = { + os.path.join(name, 'direction'): 'out' + for name, ro in self.component.allGpios() if not ro + } + self.writeConfig(path, data) + super(ScdKernelDriver, self).finish() + + def reset(self, value): + path = self.getSysfsPath() + if inSimulation(): + resets = self.component.getSysfsResetNameList() + logging.debug('reseting devices %s', resets) + return + for reset in self.component.getSysfsResetNameList(): + activeLow = False + with open(os.path.join(path, reset, 'active_low')) as f: + activeLow = bool(int(f.read(), 0)) + with open(os.path.join(path, reset, 'value'), 'w') as f: + f.write('1' if value != activeLow else '0') # logical xor + + def resetIn(self): + self.reset(True) + + def resetOut(self): + self.reset(False) + +class Scd(PciComponent): + BusTweak = namedtuple('BusTweak', 'bus, addr, t, datr, datw') + def __init__(self, addr, newDriver=False): + super(Scd, self).__init__(addr) + self.addDriver(KernelDriver, 'scd') + if newDriver: + self.addDriver(ScdHwmonKernelDriver) + self.rwCls = ScdSysfsRW + else: + self.addDriver(ScdKernelDriver) + self.rwCls = ScdSysfsOldRW + self.masters = OrderedDict() + self.resets = [] + self.gpios = [] + self.qsfps = OrderedDict() + self.sfps = OrderedDict() + self.leds = [] + self.tweaks = [] + + def addBusTweak(self, bus, addr, t=1, datr=1, datw=3): + self.tweaks.append(Scd.BusTweak(bus, addr, t, datr, datw)) + + def addSmbusMaster(self, addr, id, bus=8): + self.masters[addr] = { + 'id': id, + 'bus': bus, + } + + def addSmbusMasterRange(self, addr, count, spacing=0x100, bus=8): + addrs = range(addr, addr + (count + 1) * spacing, spacing) + for i, addr in enumerate(addrs, 0): + self.addSmbusMaster(addr, i, bus) + + def addLed(self, addr, name): + self.leds += [(addr, name)] + + def addLeds(self, leds): + self.leds += leds + + def addReset(self, gpio): + self.resets += [gpio] + + def addResets(self, gpios): + self.resets += gpios + + def addGpio(self, gpio): + self.gpios += [gpio] + + def addGpios(self, gpios): + self.gpios += gpios + + def addQsfp(self, addr, xcvrId, bus, eepromAddr=0x50): + self.qsfps[addr] = { + 'id': xcvrId, + 'bus': bus, + 'gpios': [ + Gpio(0, True, True), + Gpio(2, True, True), + Gpio(3, True, False), + Gpio(5, True, False), + Gpio(6, False, False), + Gpio(7, False, False), + Gpio(8, False, True), + ] + } + return ScdKernelXcvr(xcvrId, Xcvr.QSFP, eepromAddr, bus, self.drivers[1], + self.rwCls) + + def addSfp(self, addr, xcvrId, bus, eepromAddr=0x50): + self.sfps[addr] = { + 'id': xcvrId, + 'bus': bus, + 'gpios': [ + Gpio(0, True, False), + Gpio(1, True, False), + Gpio(2, True, True), + Gpio(3, True, False), + Gpio(4, True, False), + Gpio(5, True, False), + Gpio(6, False, False), + Gpio(7, False, False), + Gpio(8, False, False), + ] + } + return ScdKernelXcvr(xcvrId, Xcvr.SFP, eepromAddr, bus, self.drivers[1], + self.rwCls) + + def createPsu(self, psuId, statusSupported): + return ScdKernelPsu(psuId, self.drivers[1], self.rwCls, statusSupported) + + def allGpios(self): + def zipXcvr(xcvrType, gpio_names, entries): + res = [] + for data in entries.values(): + for name, gpio in zip(gpio_names, data['gpios']): + res += [ ("%s%d_%s" % (xcvrType, data['id'], name), gpio.ro) ] + return res + + sfp_names = [ + "rxlos", "txfault", "present", "rxlos_changed", "txfault_changed", + "present_changed", "txdisable", "rate_select0", "rate_select1", + ] + + qsfp_names = [ + "interrupt", "present", "interrupt_changed", "present_changed", + "lp_mode", "reset", "modsel", + ] + + gpios = [] + gpios += zipXcvr("sfp", sfp_names, self.sfps) + gpios += zipXcvr("qsfp", qsfp_names, self.qsfps) + gpios += [ (gpio.name, gpio.ro) for gpio in self.gpios ] + gpios += [ (reset.name, False) for reset in self.resets ] + return gpios + + def getSysfsResetNameList(self, xcvrs=True): + entries = [reset.name for reset in self.resets] + if xcvrs: + entries += ['qsfp%d_reset' % data['id'] for data in self.qsfps.values()] + return entries + diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/core/__init__.py b/platform/broadcom/sonic-platform-modules-arista/arista/core/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/core/component.py b/platform/broadcom/sonic-platform-modules-arista/arista/core/component.py new file mode 100644 index 000000000000..c05333dd6a87 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/core/component.py @@ -0,0 +1,109 @@ +from __future__ import print_function +from collections import defaultdict + +from .driver import Driver +from .utils import flatten + +import os + +DEFAULT_WAIT_TIMEOUT = 15 +ASIC_YIELD_TIME = os.getenv( 'ASIC_YIELD_TIME', 2 ) + +class Priority: + DEFAULT = 0 + BACKGROUND = 1 + +class Component(object): + def __init__(self, priority=Priority.DEFAULT, **kwargs): + self.components = defaultdict(list) + self.drivers = [] + self.priority = priority + for key, value in kwargs.items(): + setattr(self, key, value) + self.params = kwargs.keys() + + def __str__(self): + kwargs = ['%s=%s' % (k, getattr(self, k)) for k in self.params] + return '%s(%s)' % (self.__class__.__name__, ', '.join(kwargs)) + + def addComponents(self, components): + assert all(isinstance(c, Component) for c in components) + for component in components: + component.priority = max(component.priority, self.priority) + self.components[component.priority].append(component) + return self + + def addComponent(self, component): + assert isinstance(component, Component) + component.priority = max(component.priority, self.priority) + self.components[component.priority].append(component) + return self + + def addDriver(self, driverCls, *args, **kwargs): + assert issubclass(driverCls, Driver) + self.drivers += [driverCls(self, *args, **kwargs)] + return self + + def setup(self): + for driver in self.drivers: + driver.setup() + for driver in self.drivers: + driver.finish() + + def finish(self, priority=Priority.DEFAULT): + # underlying component are initialized recursively but require the parent to + # be fully initialized + for component in self.components[priority]: + component.setup() + for component in self.components[Priority.DEFAULT]: + component.finish(priority) + + def clean(self): + for component in flatten(self.components.values()): + component.clean() + for driver in reversed(self.drivers): + driver.clean() + + def resetIn(self): + for component in flatten(self.components.values()): + component.resetIn() + for driver in reversed(self.drivers): + driver.resetIn() + + def resetOut(self): + for driver in self.drivers: + driver.resetOut() + for component in flatten(self.components.values()): + component.resetOut() + + def waitForIt(self, timeout=DEFAULT_WAIT_TIMEOUT): + for component in flatten(self.components.values()): + component.waitForIt(timeout) + + def _dumpDrivers(self, depth, prefix): + if len(self.drivers) == 1: + self.drivers[0].dump(prefix=' => ') + elif self.drivers: + spacer = ' ' * (depth * 3) + print('%s%sdrivers:' % (spacer, prefix)) + for driver in self.drivers: + driver.dump(depth + 1) + + def _dumpNode(self, depth, prefix): + depth += 1 + spacer = ' ' * (depth * 3) + if self.drivers: + self._dumpDrivers(depth, prefix) + print('%s%scomponents:' % (spacer, prefix)) + for component in flatten(self.components.values()): + component.dump(depth + 1) + + def dump(self, depth=0, prefix=' - '): + spacer = ' ' * (depth * 3) + end = '' if len(self.drivers) == 1 else '\n' + print('%s%s%s' % (spacer, prefix,self), end=end) + if self.components: + self._dumpNode(depth, prefix) + else: + self._dumpDrivers(depth, prefix) + diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/core/driver.py b/platform/broadcom/sonic-platform-modules-arista/arista/core/driver.py new file mode 100644 index 000000000000..ad3ffc0ffc67 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/core/driver.py @@ -0,0 +1,106 @@ +from __future__ import print_function + +import logging +import os +import subprocess +import time + +from .utils import inDebug, inSimulation + +def modprobe(name, args=None): + logging.debug('loading module %s', name) + if args is None: + args = [] + args = ['modprobe', name.replace('-', '_')] + args + if inDebug(): + args += [ 'dyndbg=+pf' ] + if inSimulation(): + logging.debug('exec: %s', ' '.join(args)) + else: + subprocess.check_call(args) + +def rmmod(name): + logging.debug('unloading module %s', name) + args = ['modprobe', '-r', name.replace('-', '_')] + if inSimulation(): + logging.debug('exec: %s', ' '.join(args)) + else: + subprocess.check_call(args) + +def isModuleLoaded(name): + with open('/proc/modules') as f: + start = '%s ' % name.replace('-', '_') + for line in f.readlines(): + if line.startswith(start): + return True + return False + +class Driver(object): + def __init__(self, component): + self.component = component + + def setup(self): + pass + + def finish(self): + pass + + def clean(self): + pass + + def resetIn(self): + pass + + def resetOut(self): + pass + + def dump(self, depth=0, prefix=' - '): + spacer = ' ' * (depth * 3) + print('%s%s%s' % (spacer, prefix, self)) + +class KernelDriver(Driver): + # TODO: handle multiple kernel modules + def __init__(self, component, module, waitFile=None, args=None): + super(KernelDriver, self).__init__(component) + self.component = component + self.module = module + self.args = args if args is not None else [] + self.waitFile = waitFile + + def waitFileReady(self): + if not self.waitFile: + return + + logging.debug('Starting driver. Waiting file %s.', self.waitFile) + + count = 0 + while not os.path.exists(self.waitFile) and count <= 20: + time.sleep(0.05) + count = count + 1 + logging.debug('Starting driver. Waiting file %s attempt %d.', + self.waitFile, count) + if not os.path.exists(self.waitFile): + logging.error('Starting driver. Waiting file %s failed.', + self.waitFile) + + def setup(self): + modprobe(self.module, self.args) + self.waitFileReady() + + def clean(self): + if self.loaded(): + try: + rmmod(self.module) + except Exception as e: + logging.error('Failed to unload %s: %s', self.module, e) + else: + logging.debug('Module %s is not loaded', self.module) + + def loaded(self): + return isModuleLoaded(self.module) + + def getSysfsPath(self): + raise NotImplementedError + + def __str__(self): + return '%s(%s)' % (self.__class__.__name__, self.module) diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/core/inventory.py b/platform/broadcom/sonic-platform-modules-arista/arista/core/inventory.py new file mode 100644 index 000000000000..b5f9b453bca0 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/core/inventory.py @@ -0,0 +1,101 @@ +from collections import defaultdict + +class Xcvr(object): + + SFP = 0 + QSFP = 1 + + def __init__(self, portNum, xcvrType, eepromAddr, bus): + self.portNum = portNum + self.xcvrType = xcvrType + self.bus = bus + self.eepromAddr = eepromAddr + + def getPresence(self): + raise NotImplementedError() + + def getLowPowerMode(self): + raise NotImplementedError() + + def setLowPowerMode(self, value): + raise NotImplementedError() + + def reset(self, value): + raise NotImplementedError() + +class Psu(object): + def getPresence(self): + raise NotImplementedError() + + def getStatus(self): + raise NotImplementedError() + +class Inventory(object): + def __init__(self): + self.sfpRange = [] + self.qsfpRange = [] + self.allXcvrsRange = [] + + self.portStart = None + self.portEnd = None + + self.xcvrs = {} + + self.xcvrLeds = defaultdict(list) + self.statusLeds = [] + + self.psus = [] + + def freeze(self): + # XXX: compute the range and some basic information from the various + # collections present in the inventory + # XXX: try to avoid that actually + pass + + def addPorts(self, sfps=None, qsfps=None): + if sfps: + self.sfpRange = sfps + if qsfps: + self.qsfpRange = qsfps + + self.allXcvrsRange = sorted(self.sfpRange + self.qsfpRange) + self.portStart = self.allXcvrsRange[0] + self.portEnd = self.allXcvrsRange[-1] + + def addXcvr(self, xcvr): + self.xcvrs[xcvr.portNum] = xcvr + + def getXcvrs(self): + return self.xcvrs + + def getXcvr(self, xcvrId): + return self.xcvrs[xcvrId] + + def getPortToEepromMapping(self): + eepromPath = '/sys/class/i2c-adapter/i2c-{0}/{0}-{1:04x}/eeprom' + return { xcvrId : eepromPath.format(xcvr.bus, xcvr.eepromAddr) + for xcvrId, xcvr in self.xcvrs.items() } + + def getPortToI2cAdapterMapping(self): + return { xcvrId : xcvr.bus for xcvrId, xcvr in self.xcvrs.items() } + + def addXcvrLed(self, xcvrId, name): + self.xcvrLeds[xcvrId].append(name) + + def addStatusLed(self, name): + self.statusLeds.append(name) + + def addStatusLeds(self, names): + self.statusLeds.extend(names) + + def addPsus(self, psus): + self.psus = psus + + def getPsu(self, index): + return self.psus[index] + + def getNumPsus(self): + return len(self.psus) + + + diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/core/platform.py b/platform/broadcom/sonic-platform-modules-arista/arista/core/platform.py new file mode 100644 index 000000000000..da27c7ba2200 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/core/platform.py @@ -0,0 +1,125 @@ +from __future__ import print_function + +import logging +import subprocess +import os +import sys + +from collections import OrderedDict, namedtuple, defaultdict + +from .inventory import Inventory +from .component import Component, Priority +from .utils import simulateWith +from .driver import modprobe, rmmod, KernelDriver + +from . import prefdl + +platforms = {} +syseeprom = None + +host_prefdl_path = '/host/.system-prefdl' +fmted_prefdl_path = '/etc/sonic/.syseeprom' + +def formatPrefdlData(data): + formatDict = { + "MAC": ["MAC", "MacAddrBase", "Mac"], + "SKU": ["SKU", "Sku"], + "SerialNumber": ["SerialNumber"], + } + fdata = { } + for k, v in data.items(): + for kfmt, vfmt in formatDict.items(): + if k in vfmt: + if kfmt == "MAC": + val = prefdl.showMac(v) + else: + val = v + fdata[kfmt] = val + return fdata + +def writeFormattedPrefdl(pfdl, f): + fdata = formatPrefdlData(pfdl.data()) + with open(f, 'w+') as fp: + for k, v in fdata.items(): + fp.write("%s: %s\n" % (k, v)) + +def readPrefdl(): + if os.path.exists(fmted_prefdl_path): + with open(fmted_prefdl_path) as fp: + logging.debug('reading system eeprom from %s', fmted_prefdl_path) + return prefdl.PreFdlFromFile(fp) + + if os.path.exists(host_prefdl_path): + with open(host_prefdl_path) as fp: + logging.debug('reading system eeprom from %s', host_prefdl_path) + pfdl = prefdl.PreFdlFromFile(fp) + writeFormattedPrefdl(pfdl, fmted_prefdl_path) + with open(fmted_prefdl_path) as fp: + return prefdl.PreFdlFromFile(fp) + + modprobe('eeprom') + for addr in ['1-0052']: + eeprompath = os.path.join('/sys/bus/i2c/drivers/eeprom', addr, 'eeprom') + if not os.path.exists(eeprompath): + continue + try: + with open(eeprompath) as fp: + logging.debug('reading system eeprom from %s', eeprompath) + pfdl = prefdl.decode(fp) + pfdl.writeToFile(fmted_prefdl_path) + return pfdl + except Exception as e: + logging.warn('could not obtain prefdl from %s', eeprompath) + logging.warn('error seen: %s', e) + raise RuntimeError("Could not find valid system eeprom") + +def getPrefdlDataSim(): + logging.debug('bypass prefdl reading by returning default values') + return {'SKU': 'simulation'} + +@simulateWith(getPrefdlDataSim) +def getPrefdlData(): + return readPrefdl().data() + +def getSysEeprom(): + global syseeprom + if not syseeprom: + syseeprom = getPrefdlData() + assert 'SKU' in syseeprom + return syseeprom + +def detectPlatform(): + return getSysEeprom()['SKU'] + +def getPlatform(name=None): + if name == None: + name = detectPlatform() + return platforms[name]() + +def getPlatforms(): + return platforms + +def registerPlatform(skus): + global platforms + def wrapper(cls): + if isinstance(skus, list): + for sku in skus: + platforms[sku] = cls + else: + platforms[skus] = cls + return cls + return wrapper + +class Platform(Component): + def __init__(self): + super(Platform, self).__init__() + self.addDriver(KernelDriver, 'eeprom') + self.addDriver(KernelDriver, 'i2c-dev') + self.inventory = Inventory() + + def setup(self, priority=Priority.DEFAULT): + super(Platform, self).setup() + super(Platform, self).finish(priority) + + def getInventory(self): + return self.inventory diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/core/prefdl.py b/platform/broadcom/sonic-platform-modules-arista/arista/core/prefdl.py new file mode 100755 index 000000000000..036f5be702cd --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/core/prefdl.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 Arista Networks, Inc. +# +# 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 3 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, see . + +import re +import struct +import sys +import zlib + +def showMac( m ): + return ":".join([m[0:2], m[2:4], m[4:6], m[6:8], m[8:10], m[10:12]]) + +typeMap = { + "END" : ( "00", None, None, None, False ), + "SKU" : ( "03", None, None, None, False ), + "MAC" : ( "05", None, showMac, None, False ), + "SerialNumber" : ( "0E", None, None, None, False ), +} + +idToNameMap = {} +for k, v in typeMap.items(): + idToNameMap[ v[0] ] = k + +def crc32( data ): + return struct.unpack("I",struct.pack("i",zlib.crc32( data )))[0] + +def validSerial( x ): + x = x.replace( " ", "" ) + x = x.replace( "-", "" ) + # All serial numbers are upper case + x = x.upper() + if re.compile( "[A-Z]{3}\d{4}[A-Z0-9]{4}$" ).match( x ): + return x + return None + +class PreFdlField( ): + def __init__( self, name, valid, show, optionName, data=None, append=False ): + self.name = name + if valid: + self.valid = valid + else: + self.valid = lambda x: x + self.show = show + self.optionName = optionName + self.data = [] + self.append = append + if data: + self.dataIs( data ) + + def dataIs( self, data ): + vd = self.valid( data ) + if not vd: + raise InvalidPrefdlData( "Invalid %s: %s" % ( self.name, data ) ) + if self.append: + self.data.append( vd ) + else: + self.data = [ vd ] + +class TlvField( PreFdlField ): + def __init__( self, name ): + args = typeMap.get( name ) + valid = None + show = None + optionName = None + append = False + if args: + self.id, valid, show, optionName, append = args + PreFdlField.__init__( self, name, valid, show, optionName, append=append ) + +class PreFdl(): + def __init__( self, fp=None, preFdlStr=None, version="0002" ): + # populate the required fields + self.requiredFields = [] + + if version == "0002": + preFdlStr, offset = self.initPreFdl2( fp, preFdlStr ) + elif version == "0003": + preFdlStr, offset = self.initPreFdl3( fp, preFdlStr ) + else: + raise NotImplementedError( + "Only Prefdl data format version 0002 or 0003 are supported" ) + + # populate the tlv fileds + self.tlvFields = {} + for k in typeMap.keys(): + self.tlvFields[ k ] = TlvField( k ) + + # create the map option to field + self.optionMap = {} + for f in self.requiredFields + self.tlvFields.values(): + # Do not add the option from TLV if already added by required fields + if f.optionName and f.optionName not in self.optionMap: + self.optionMap[ f.optionName ] = f + + # save the current tlv fields + if fp: + while True: + tlv = fp.read( 6 ) + ( id, lengthStr ) = ( tlv[0:2], tlv[2:6] ) + length = int( lengthStr, base=16 ) + bytes = fp.read( length ) + what = None if id not in idToNameMap.keys() else idToNameMap[ id ] + if what and what != "END": + self.tlvFields[ what ].dataIs( bytes ) + preFdlStr += tlv + bytes + offset += 6 + length + if what == "END": + # End of the tlv list + break + self.crc = fp.read( 8 ) + # Check the CRC + computed = crc32( preFdlStr ) + if int( self.crc, 16 ) != computed: + raise Exception( "Invalid CRC -- saw %s expected %8X" % + ( self.crc, computed ) ) + + # Initialize and parse fixed section for prefdl version 2. Return the offset + # to where the TLV section starts. + def initPreFdl2( self, fp, preFdlStr ): + # if we start with an existing file + if fp: + # if no preFdlStr is specified, read the fixed section, 30 bytes. + # Otherwise, only the 4 byte data version section was written and + # read the remaining 26 bytes from the fixed section. + if not preFdlStr: + preFdlStr = fp.read( 30 ).strip() + elif preFdlStr == "0002": + preFdlStr += fp.read( 26 ).strip() + else: + raise ValueError( "preFdlStr arg has invalid data format" ) + if len( preFdlStr ) < 12: + fatal( "prefdl is too short exiting" ) + data = None if not preFdlStr else preFdlStr[ 16:16 + 11 ] + self.requiredFields.append( + PreFdlField( "SerialNumber", validSerial, None, None, data ) ) + return preFdlStr, 30 + + # Initialize and parse fixed section for prefdl version 3. Return the offset + # to where the TLV section starts. + def initPreFdl3( self, fp, preFdlStr ): + # if we start with an existing file + currPtr = 0 + if fp and not preFdlStr: + preFdlStr = fp.read( 4 ).strip() + if len( preFdlStr ) < 4: + fatal( "prefdl is too short exiting" ) + return preFdlStr, 4 + + def data( self ): + res = {} + for f in self.requiredFields + self.tlvFields.values(): + for d in f.data: + dStr = d if f.show is None else f.show( d ) + res[f.name] = dStr + return res + + def show( self ): + for k, v in self.data().items(): + print("%s: %s" % (k, v)) + + def writeToFile(self, f): + with open(f, 'w+') as fp: + for k, v in self.data().items(): + fp.write("%s: %s\n" % (k, v)) + + def getField( self, name ): + return self.data().get( name, None ) + + def getCrc( self ): + return self.crc + +class PreFdlFromFile(): + def __init__(self, fp): + self._data = {} + for line in fp: + key, val = line.strip().split(': ', 1) + self._data[key] = val + self.crc = self._data.pop('Crc', -1) + + def data(self): + return self._data + + def show(self): + for key, val in self._data.items(): + print("%s: %s" % (key, val)) + + def getField(self, name): + return self._data[name] + + def getCrc(self): + return self.crc + +def decode( fp ): + data = fp.read( 4 ) + data = data.strip() + # For format 0002 and more recent fdls use the new Prefdl class + if data not in ( "0002", "0003" ): + raise ValueError + return PreFdl( fp, data, data ) + +def main(): + output = sys.argv[1] + + if output == "-": + fp = sys.stdin + else: + fp = file( output, "r" ) + + decode( fp ).show() + +if __name__ == "__main__": + main() diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/core/types.py b/platform/broadcom/sonic-platform-modules-arista/arista/core/types.py new file mode 100644 index 000000000000..ab47cabff080 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/core/types.py @@ -0,0 +1,29 @@ +from collections import namedtuple + +Register = namedtuple("Register", ["addr", "ro"]) +NamedRegister = namedtuple("NamedRegister", Register._fields + ("name", )) + +Gpio = namedtuple("Gpio", ["bit", "ro", "activeLow"]) +NamedGpio = namedtuple("NamedGpio", ("addr",) + Gpio._fields + ("name",)) + +ResetGpio = namedtuple("ResetGpio", ["addr", "bit", "activeLow", "name"]) + +class I2cAddr(object): + def __init__(self, bus, address): + self.bus = bus + self.address = address + + def __str__(self): + return '%d-00%02x' % (self.bus, self.address) + +class PciAddr(object): + def __init__(self, domain=0, bus=0, device=0, func=0): + self.domain = domain + self.bus = bus + self.device = device + self.func = func + + def __str__(self): + return '%04x:%02x:%02x.%d' % (self.domain, self.bus, self.device, self.func) + + diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/core/utils.py b/platform/broadcom/sonic-platform-modules-arista/arista/core/utils.py new file mode 100644 index 000000000000..c560accbe010 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/core/utils.py @@ -0,0 +1,129 @@ +import logging +import fcntl + +from functools import wraps + +def sysfsFmtHex(x): + return "0x%08x" % x + +def sysfsFmtDec(x): + return "%d" % x + +def sysfsFmtStr(x): + return str(x) + +def incrange(start, stop): + return list(range(start, stop + 1)) + +def flatten(nestedList): + return [val for sublist in nestedList for val in sublist] + +def klog(msg, level=2, *args): + try: + with open('/dev/kmsg', 'w') as f: + f.write('<%d>arista: %s\n' % (level, msg % tuple(*args))) + except: + pass + +class FileLock: + def __init__(self, lock_file): + self.f = open(lock_file, 'w') + + def lock(self): + fcntl.flock(self.f, fcntl.LOCK_EX) + + def unlock(self): + fcntl.flock(self.f, fcntl.LOCK_UN) + self.f.close() + + def __enter__(self): + self.lock() + + def __exit__(self, exc_type, exc_val, traceback): + self.unlock() + +class NoopObj(object): + def __init__(self, *args, **kwargs): + self.name = self.__class__.__name__ + self.classStr = '%s(%s)' % (self.name, self._fmtArgs(*args, **kwargs)) + logging.debug(self.classStr) + + def _fmtArgs(self, *args, **kwargs): + kw = ['%s=%s' % (k,v) for k, v in kwargs.items()] + return ', '.join(list(map(str, args)) + kw) + + def noop(self, attr): + def wrapped(*args, **kwargs): + funcStr = '%s(%s)' % (attr, self._fmtArgs(*args, **kwargs)) + logging.debug('%s.%s', self.classStr, funcStr) + return wrapped + + def __getattr__(self, attr): + return self.noop(attr) + +CMDLINE_PATH = '/proc/cmdline' + +cmdlineDict = {} +def getCmdlineDict(): + global cmdlineDict + + if cmdlineDict: + return cmdlineDict + + data = {} + with open(CMDLINE_PATH) as f: + for entry in f.read().split(): + idx = entry.find('=') + if idx == -1: + data[entry] = None + else: + data[entry[:idx]] = entry[idx+1:] + + cmdlineDict = data + return data + +# debug flag, if enabled should use the most tracing possible +debug = False + +# force simulation to be True if not on a Arista box +simulation = True + +# simulation related globals +SMBus = None + +def inDebug(): + return debug + +def inSimulation(): + return simulation + +def simulateWith(simulatedFunc): + def simulateThisFunc(func): + @wraps(func) + def funcWrapper(*args, **kwargs): + if inSimulation(): + return simulatedFunc(*args, **kwargs) + return func(*args, **kwargs) + return funcWrapper + return simulateThisFunc + +def libraryInit(): + global simulation, debug, SMBus + + cmdline = getCmdlineDict() + if "Aboot" in cmdline: + simulation = False + + if "arista-debug" in cmdline: + debug = True + + if simulation: + SMBus = type('SMBus', (NoopObj,), {}) + else: + try: + from smbus import SMBus + except ImportError: + pass + +libraryInit() + diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/platforms/__init__.py b/platform/broadcom/sonic-platform-modules-arista/arista/platforms/__init__.py new file mode 100644 index 000000000000..14e93ec982c9 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/platforms/__init__.py @@ -0,0 +1,5 @@ + +from . import a7050qx32 +from . import a7050qx32s +from . import a7060cx32s +from . import a7260cx364 diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/platforms/a7050qx32.py b/platform/broadcom/sonic-platform-modules-arista/arista/platforms/a7050qx32.py new file mode 100644 index 000000000000..afc4dd2bcf49 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/platforms/a7050qx32.py @@ -0,0 +1,94 @@ +from ..core.platform import registerPlatform, Platform +from ..core.driver import KernelDriver +from ..core.utils import incrange +from ..core.types import PciAddr, I2cAddr, Gpio, NamedGpio, ResetGpio +from ..core.component import Priority + +from ..components.common import SwitchChip, I2cKernelComponent +from ..components.scd import Scd +from ..components.ds460 import Ds460 + +@registerPlatform('DCS-7050QX-32') +class Cloverdale(Platform): + def __init__(self): + super(Cloverdale, self).__init__() + + self.fanCount = 4 + self.qsfp40gAutoRange = incrange(1, 24) + self.qsfp40gOnlyRange = incrange(25, 32) + self.allQsfps = sorted(self.qsfp40gAutoRange + self.qsfp40gOnlyRange) + self.sfpRange = [] + + self.inventory.addPorts(qsfps=self.allQsfps) + + self.addDriver(KernelDriver, 'raven-fan-driver', '/sys/class/hwmon/hwmon1') + + switchChip = SwitchChip(PciAddr(bus=0x02)) + self.addComponent(switchChip) + + scd = Scd(PciAddr(bus=0x04)) + self.addComponent(scd) + + scd.addComponents([ + I2cKernelComponent(I2cAddr(2, 0x4c), 'max6658', '/sys/class/hwmon/hwmon2'), + I2cKernelComponent(I2cAddr(3, 0x48), 'lm73', '/sys/class/hwmon/hwmon3'), + Ds460(I2cAddr(5, 0x58), priority=Priority.BACKGROUND), + Ds460(I2cAddr(6, 0x58), priority=Priority.BACKGROUND), + + # Due to a risk of an unrecoverable firmware corruption when a pmbus + # transaction is done at the same moment of the poweroff, the handling of + # the DPM is disabled. If you want rail information use it at your own risk + #I2cKernelComponent(I2cAddr(3, 0x4e), 'pmbus'), # ucd90120A + #I2cKernelComponent(I2cAddr(7, 0x4e), 'pmbus'), # ucd90120A + ]) + + scd.addSmbusMasterRange(0x8000, 5) + + scd.addLeds([ + (0x6050, 'status'), + (0x6060, 'fan_status'), + (0x6070, 'psu1'), + (0x6080, 'psu2'), + (0x6090, 'beacon'), + ]) + self.inventory.addStatusLeds(['status', 'fan_status', 'psu1', + 'psu2']) + + scd.addResets([ + ResetGpio(0x4000, 0, False, 'switch_chip_reset'), + ResetGpio(0x4000, 2, False, 'phy1_reset'), + ResetGpio(0x4000, 3, False, 'phy2_reset'), + ResetGpio(0x4000, 4, False, 'phy3_reset'), + ResetGpio(0x4000, 5, False, 'phy4_reset'), + ]) + + scd.addGpios([ + NamedGpio(0x5000, 0, True, False, "psu1_present"), + NamedGpio(0x5000, 1, True, False, "psu2_present"), + ]) + self.inventory.addPsus([scd.createPsu(1, False), scd.createPsu(2, False)]) + + addr = 0x6100 + for xcvrId in self.qsfp40gAutoRange: + for laneId in incrange(1, 4): + name = "qsfp%d_%d" % (xcvrId, laneId) + scd.addLed(addr, name) + self.inventory.addXcvrLed(xcvrId, name) + addr += 0x10 + + addr = 0x6720 + for xcvrId in self.qsfp40gOnlyRange: + name = "qsfp%d" % xcvrId + scd.addLed(addr, name) + self.inventory.addXcvrLed(xcvrId, name) + addr += 0x30 if xcvrId % 2 else 0x50 + + addr = 0x5010 + bus = 10 + for xcvrId in self.allQsfps: + xcvr = scd.addQsfp(addr, xcvrId, bus) + scd.addComponent(I2cKernelComponent( + I2cAddr(bus, xcvr.eepromAddr), 'sff8436')) + self.inventory.addXcvr(xcvr) + addr += 0x10 + bus += 1 diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/platforms/a7050qx32s.py b/platform/broadcom/sonic-platform-modules-arista/arista/platforms/a7050qx32s.py new file mode 100644 index 000000000000..a95dba7e4200 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/platforms/a7050qx32s.py @@ -0,0 +1,116 @@ +from ..core.platform import registerPlatform, Platform +from ..core.driver import KernelDriver +from ..core.utils import incrange +from ..core.types import PciAddr, I2cAddr, Gpio, NamedGpio, ResetGpio +from ..core.component import Priority + +from ..components.common import SwitchChip, I2cKernelComponent +from ..components.scd import Scd +from ..components.ds125br import Ds125Br + +@registerPlatform('DCS-7050QX-32S') +class Clearlake(Platform): + def __init__(self): + super(Clearlake, self).__init__() + + # FIXME: due to an issue with the kernel drivers, the sfp ports are disabled + # self.sfpRange = incrange(1, 4) + self.sfpRange = [] + self.qsfp40gAutoRange = incrange(5, 28) + self.qsfp40gOnlyRange = incrange(29, 36) + self.allQsfps = sorted(self.qsfp40gAutoRange + self.qsfp40gOnlyRange) + + self.inventory.addPorts(qsfps=self.allQsfps) + + self.addDriver(KernelDriver, 'crow-fan-driver', '/sys/class/hwmon/hwmon1') + + switchChip = SwitchChip(PciAddr(bus=0x01)) + self.addComponent(switchChip) + + scd = Scd(PciAddr(bus=0x02)) + self.addComponent(scd) + + scd.addComponents([ + I2cKernelComponent(I2cAddr(2, 0x4c), 'max6658', '/sys/class/hwmon/hwmon2'), + I2cKernelComponent(I2cAddr(3, 0x4c), 'max6658', '/sys/class/hwmon/hwmon3'), + I2cKernelComponent(I2cAddr(3, 0x60), 'crow_cpld', '/sys/class/hwmon/hwmon4'), + # Handling of the DPM is disabled because this functionality is unstable. + #I2cKernelComponent(I2cAddr(3, 0x4e), 'pmbus', + # priority=Priority.BACKGROUND), # ucd90120A + I2cKernelComponent(I2cAddr(5, 0x58), 'pmbus', + priority=Priority.BACKGROUND), + I2cKernelComponent(I2cAddr(6, 0x58), 'pmbus', + priority=Priority.BACKGROUND), + # Handling of the DPM is disabled because this functionality is unstable. + #I2cKernelComponent(I2cAddr(7, 0x4e), 'pmbus', + # priority=Priority.BACKGROUND), # ucd90120A + Ds125Br(I2cAddr(8, 0xff)), + ]) + + scd.addSmbusMasterRange(0x8000, 6) + + scd.addLeds([ + (0x6050, 'status'), + (0x6060, 'fan_status'), + (0x6070, 'psu1'), + (0x6080, 'psu2'), + (0x6090, 'beacon'), + ]) + self.inventory.addStatusLeds(['status', 'fan_status', 'psu1', + 'psu2']) + + scd.addReset(ResetGpio(0x4000, 0, False, 'switch_chip_reset')) + + scd.addGpios([ + NamedGpio(0x5000, 0, True, False, "psu1_present"), + NamedGpio(0x5000, 1, True, False, "psu2_present"), + NamedGpio(0x6940, 0, False, False, "mux"), # FIXME: oldSetup order/name + ]) + self.inventory.addPsus([scd.createPsu(1, False), scd.createPsu(2, False)]) + + addr = 0x6100 + for xcvrId in self.qsfp40gAutoRange: + for laneId in incrange(1, 4): + name = "qsfp%d_%d" % (xcvrId, laneId) + scd.addLed(addr, name) + self.inventory.addXcvrLed(xcvrId, name) + addr += 0x10 + + addr = 0x6720 + for xcvrId in self.qsfp40gOnlyRange: + name = "qsfp%d" % xcvrId + scd.addLed(addr, name) + self.inventory.addXcvrLed(xcvrId, name) + addr += 0x30 if xcvrId % 2 else 0x50 + + addr = 0x6900 + for xcvrId in self.sfpRange: + name = "sfp%d" % xcvrId + scd.addLed(addr, name) + self.inventory.addXcvrLed(xcvrId, name) + addr += 0x10 + + addr = 0x5010 + bus = 10 + for xcvrId in self.allQsfps: + xcvr = scd.addQsfp(addr, xcvrId, bus) + self.inventory.addXcvr(xcvr) + scd.addComponent(I2cKernelComponent( + I2cAddr(bus, xcvr.eepromAddr), 'sff8436')) + addr += 0x10 + bus += 1 + + addr = 0x5210 + bus = 42 + for xcvrId in sorted(self.sfpRange): + xcvr = scd.addSfp(addr, xcvrId, bus) + self.inventory.addXcvr(xcvr) + scd.addComponent(I2cKernelComponent( + I2cAddr(bus, xcvr.eepromAddr), 'sff8436')) + addr += 0x10 + bus += 1 + +@registerPlatform('DCS-7050QX2-32S') +class ClearlakePlus(Clearlake): + pass + diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/platforms/a7060cx32s.py b/platform/broadcom/sonic-platform-modules-arista/arista/platforms/a7060cx32s.py new file mode 100644 index 000000000000..9547fe9f0a39 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/platforms/a7060cx32s.py @@ -0,0 +1,102 @@ +from ..core.platform import registerPlatform, Platform +from ..core.driver import KernelDriver +from ..core.utils import incrange +from ..core.types import PciAddr, I2cAddr, Gpio, NamedGpio, ResetGpio +from ..core.component import Priority + +from ..components.common import SwitchChip, I2cKernelComponent +from ..components.scd import Scd + +@registerPlatform(['DCS-7060CX-32S', 'DCS-7060CX-32S-ES']) +class Upperlake(Platform): + def __init__(self): + super(Upperlake, self).__init__() + + self.sfpRange = incrange(33, 34) + self.qsfp100gRange = incrange(1, 32) + + self.inventory.addPorts(sfps=self.sfpRange, qsfps=self.qsfp100gRange) + + self.addDriver(KernelDriver, 'crow-fan-driver') + + switchChip = SwitchChip(PciAddr(bus=0x01)) + self.addComponent(switchChip) + + scd = Scd(PciAddr(bus=0x02), newDriver=True) + self.addComponent(scd) + + scd.addComponents([ + I2cKernelComponent(I2cAddr(2, 0x1a), 'max6697', '/sys/class/hwmon/hwmon1'), + I2cKernelComponent(I2cAddr(3, 0x4c), 'max6658', '/sys/class/hwmon/hwmon2'), + I2cKernelComponent(I2cAddr(3, 0x60), 'crow_cpld', '/sys/class/hwmon/hwmon3'), + # Handling of the DPM is disabled because this functionality is unstable. + #I2cKernelComponent(I2cAddr(3, 0x4e), 'pmbus', + # priority=Priority.BACKGROUND), # ucd90120A + I2cKernelComponent(I2cAddr(5, 0x58), 'pmbus', + priority=Priority.BACKGROUND), + I2cKernelComponent(I2cAddr(6, 0x58), 'pmbus', + priority=Priority.BACKGROUND), + # Handling of the DPM is disabled because this functionality is unstable. + #I2cKernelComponent(I2cAddr(7, 0x4e), 'pmbus', + # priority=Priority.BACKGROUND), # ucd90120A + ]) + + scd.addSmbusMasterRange(0x8000, 5, 0x80) + + scd.addLeds([ + (0x6050, 'status'), + (0x6060, 'fan_status'), + (0x6070, 'psu1'), + (0x6080, 'psu2'), + (0x6090, 'beacon'), + ]) + self.inventory.addStatusLeds(['status', 'fan_status', 'psu1', 'psu2']) + + scd.addResets([ + ResetGpio(0x4000, 1, False, 'switch_chip_reset'), + ResetGpio(0x4000, 2, False, 'switch_chip_pcie_reset'), + ]) + + scd.addGpios([ + NamedGpio(0x5000, 0, True, False, "psu1_present"), + NamedGpio(0x5000, 1, True, False, "psu2_present"), + ]) + self.inventory.addPsus([scd.createPsu(1, False), scd.createPsu(2, False)]) + + addr = 0x6100 + for xcvrId in self.sfpRange: + name = "sfp%d" % xcvrId + scd.addLed(addr, name) + self.inventory.addXcvrLed(xcvrId, name) + addr += 0x10 + + addr = 0x6140 + for xcvrId in self.qsfp100gRange: + for laneId in incrange(1, 4): + name = "qsfp%d_%d" % (xcvrId, laneId) + scd.addLed(addr, name) + self.inventory.addXcvrLed(xcvrId, name) + addr += 0x10 + + addr = 0x5010 + bus = 10 + for xcvrId in self.sfpRange: + xcvr = scd.addSfp(addr, xcvrId, bus) + self.inventory.addXcvr(xcvr) + scd.addComponent(I2cKernelComponent( + I2cAddr(bus, xcvr.eepromAddr), 'sff8436')) + scd.addBusTweak(bus, xcvr.eepromAddr) + addr += 0x10 + bus += 1 + + addr = 0x5050 + bus = 18 + for xcvrId in self.qsfp100gRange: + xcvr = scd.addQsfp(addr, xcvrId, bus) + self.inventory.addXcvr(xcvr) + scd.addComponent(I2cKernelComponent( + I2cAddr(bus, xcvr.eepromAddr), 'sff8436')) + scd.addBusTweak(bus, xcvr.eepromAddr) + addr += 0x10 + bus += 1 + diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/platforms/a7260cx364.py b/platform/broadcom/sonic-platform-modules-arista/arista/platforms/a7260cx364.py new file mode 100644 index 000000000000..514728f12211 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/platforms/a7260cx364.py @@ -0,0 +1,106 @@ +from ..core.platform import registerPlatform, Platform +from ..core.driver import KernelDriver +from ..core.utils import incrange +from ..core.types import PciAddr, I2cAddr, Gpio, NamedGpio, ResetGpio +from ..core.component import Priority + +from ..components.common import SwitchChip, I2cKernelComponent +from ..components.scd import Scd +from ..components.ds125br import Ds125Br +from ..components.ds460 import Ds460 + +@registerPlatform('DCS-7260CX3-64') +class Gardena(Platform): + def __init__(self): + super(Gardena, self).__init__() + + self.sfpRange = incrange(65, 66) + self.qsfpRange = incrange(1, 64) + + self.inventory.addPorts(qsfps=self.qsfpRange, sfps=self.sfpRange) + + self.addDriver(KernelDriver, 'rook-fan-cpld') + self.addDriver(KernelDriver, 'rook-led-driver') + + switchChip = SwitchChip(PciAddr(bus=0x07)) + self.addComponent(switchChip) + + scd = Scd(PciAddr(bus=0x06), newDriver=True) + self.addComponent(scd) + + scd.addComponents([ + I2cKernelComponent(I2cAddr(1, 0x4c), 'max6658', '/sys/class/hwmon/hwmon1'), + I2cKernelComponent(I2cAddr(3, 0x58), 'pmbus', + priority=Priority.BACKGROUND), + I2cKernelComponent(I2cAddr(4, 0x58), 'pmbus', + priority=Priority.BACKGROUND), + ]) # Incomplete + + scd.addSmbusMasterRange(0x8000, 8, 0x80) + + scd.addResets([ + ResetGpio(0x4000, 0, False, 'switch_chip_reset'), + ResetGpio(0x4000, 1, False, 'switch_chip_pcie_reset'), + ResetGpio(0x4000, 2, False, 'security_asic_reset'), + ]) + + scd.addGpios([ + NamedGpio(0x5000, 0, True, False, "psu1_present"), + NamedGpio(0x5000, 1, True, False, "psu2_present"), + NamedGpio(0x5000, 8, True, False, "psu1_status"), + NamedGpio(0x5000, 9, True, False, "psu2_status"), + NamedGpio(0x5000, 10, True, False, "psu1_ac_status"), + NamedGpio(0x5000, 11, True, False, "psu2_ac_status"), + ]) + self.inventory.addPsus([scd.createPsu(1, True), scd.createPsu(2, True)]) + + addr = 0x6100 + for xcvrId in self.qsfpRange: + for laneId in incrange(1, 4): + name = "qsfp%d_%d" % (xcvrId, laneId) + scd.addLed(addr, name) + self.inventory.addXcvrLed(xcvrId, name) + addr += 0x10 + + addr = 0x7100 + for xcvrId in self.sfpRange: + name = "sfp%d" % xcvrId + scd.addLed(addr, name) + self.inventory.addXcvrLed(xcvrId, name) + addr += 0x10 + + addr = 0xA010 + bus = 9 + for xcvrId in sorted(self.qsfpRange): + xcvr = scd.addQsfp(addr, xcvrId, bus) + self.inventory.addXcvr(xcvr) + scd.addComponent(I2cKernelComponent( + I2cAddr(bus, xcvr.eepromAddr), 'sff8436')) + scd.addBusTweak(bus, xcvr.eepromAddr) + addr += 0x10 + bus += 1 + + addr = 0xA410 + bus = 7 + for xcvrId in sorted(self.sfpRange): + xcvr = scd.addSfp(addr, xcvrId, bus) + self.inventory.addXcvr(xcvr) + scd.addComponent(I2cKernelComponent( + I2cAddr(bus, xcvr.eepromAddr), 'sff8436')) + scd.addBusTweak(bus, xcvr.eepromAddr) + addr += 0x10 + bus += 1 + + cpld = Scd(PciAddr(bus=0xff, device=0x0b, func=3), newDriver=True) + self.addComponent(cpld) + + cpld.addSmbusMasterRange(0x8000, 4, 0x80, 4) + cpld.addComponents([ + I2cKernelComponent(I2cAddr(73, 0x4c), 'max6658', '/sys/class/hwmon/hwmon2'), + # Handling of the DPM is disabled because this functionality is unstable. + #I2cKernelComponent(I2cAddr(74, 0x4e), 'pmbus', + # priority=Priority.BACKGROUND), + I2cKernelComponent(I2cAddr(85, 0x60), 'rook_cpld', '/sys/class/hwmon/hwmon3'), + I2cKernelComponent(I2cAddr(88, 0x20), 'rook_leds'), + I2cKernelComponent(I2cAddr(88, 0x48), 'lm73'), + ]) diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/utils/__init__.py b/platform/broadcom/sonic-platform-modules-arista/arista/utils/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/utils/sonic_eeprom.py b/platform/broadcom/sonic-platform-modules-arista/arista/utils/sonic_eeprom.py new file mode 100644 index 000000000000..dbb2d39adb08 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/utils/sonic_eeprom.py @@ -0,0 +1,67 @@ +""" +This file provides helper for sonic environment + +Currently all arista switches have their eeprom at the same address and use +the same data format. Since it is not an open standard and all our platforms +need this having everything at the same place is easier. + +The eeprom plugin end up being just the following + + import arista.utils.sonic_eeprom + board = arista.utils.sonic_eeprom.getTlvInfoDecoder() + +""" + +from __future__ import absolute_import + +import StringIO +import os + +from ..core import prefdl +from ..core.platform import fmted_prefdl_path + +try: + from sonic_eeprom import eeprom_base + from sonic_eeprom import eeprom_tlvinfo +except ImportError as e: + raise ImportError (str(e) + " - required module not found") + +class board(eeprom_tlvinfo.TlvInfoDecoder): + + def __init__(self, name, path, cpld_root, ro): + self._prefdl_cache = {} + self.prefdl_path = fmted_prefdl_path + super(board, self).__init__(self.prefdl_path, 0, '', True) + + def read_eeprom(self): + with open(self.prefdl_path) as fp: + return fp.read() + + def _decode_eeprom(self, e): + pfdl = self._prefdl_cache.get(e, None) + if pfdl is not None: + return pfdl + + pfdl = prefdl.PreFdlFromFile(StringIO.StringIO(e)) + self._prefdl_cache[e] = pfdl + + return pfdl + + def decode_eeprom(self, e): + pfdl = self._decode_eeprom(e) + return pfdl.show() + + def is_checksum_valid(self, e): + pfdl = self._decode_eeprom(e) + return (True, pfdl.getCrc()) + + def serial_number_str(self, e): + pfdl = self._decode_eeprom(e) + return pfdl.getField('SerialNumber') + + def mgmtaddrstr(self,e): + pfdl = self._decode_eeprom(e) + return pfdl.getField('MAC') + +def getTlvInfoDecoder(): + return board diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/utils/sonic_leds.py b/platform/broadcom/sonic-platform-modules-arista/arista/utils/sonic_leds.py new file mode 100644 index 000000000000..ac63960f48a7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/utils/sonic_leds.py @@ -0,0 +1,99 @@ +import re +from collections import namedtuple, defaultdict + +from ..core import platform +import arista.platforms + +try: + from sonic_led import led_control_base +except ImportError as e: + raise ImportError ('%s - required module not found' % str(e)) + +Port = namedtuple('Port', ['portNum', 'lanes', 'offset', 'singular']) + +def parsePortConfig(portConfigPath): + ''' + Returns a dictionary mapping port name ("Ethernet48") to a named tuple of port + number, # of lanes, the offset (0 to 3 from the first lane in qsfp) and the + singularity of the lane (if it is in 100G/40G mode) + ''' + portMapping = {} + + with open(portConfigPath) as fp: + for line in fp: + line = line.strip() + if not line or line[0] == '#': + continue + + fields = line.split() + # "portNum" is determined from the fourth column (port), or the first number + # in the third column (alias). + # "lanes" is determined from the number of lanes in the second column. + # "offset" is determined from the second number in the third column (alias). + # "singular" is determined by if the alias has a '/' character or not. + if len(fields) < 3: + continue + name = fields[0] + lanes = len(fields[1].split(',')) + alias = fields[2] + aliasRe = re.findall(r'\d+', alias) + try: + portNum = int(fields[3]) + except IndexError: + portNum = int(aliasRe[0]) + if len(aliasRe) < 2: + offset = 0 + singular = True + else: + offset = int(aliasRe[1]) - 1 + singular = False + + portMapping[name] = Port(portNum, lanes, offset, singular) + + return portMapping + +class LedControl(led_control_base.LedControlBase): + PORT_CONFIG_PATH = '/usr/share/sonic/hwsku/port_config.ini' + LED_SYSFS_PATH = '/sys/class/leds/{0}/brightness' + + LED_COLOR_OFF = 0 + LED_COLOR_GREEN = 1 + LED_COLOR_YELLOW = 2 + + def __init__(self): + self.portMapping = parsePortConfig(self.PORT_CONFIG_PATH) + self.portSysfsMapping = defaultdict(list) + + inventory = platform.getPlatform().getInventory() + for port, names in inventory.xcvrLeds.items(): + for name in names: + self.portSysfsMapping[port].append(self.LED_SYSFS_PATH.format(name)) + + # Set status leds to green initially (Rook led driver does this automatically) + for statusLed in inventory.statusLeds: + with open(self.LED_SYSFS_PATH.format(statusLed), 'w') as fp: + fp.write('%d' % self.LED_COLOR_GREEN) + + def port_link_state_change(self, port, state): + ''' + Looks up the port in the port mapping to determine the front number and how + many subsequent LEDs should be affected (hardcoded by the port_config) + ''' + p = self.portMapping.get(port) + if not p: + return + for idx in range(p.lanes): + path = self.portSysfsMapping[p.portNum][p.offset + idx] + with open(path, 'w') as fp: + if state == 'up': + if idx == 0: + fp.write('%d' % self.LED_COLOR_GREEN) + else: + fp.write('%d' % self.LED_COLOR_YELLOW) + elif state == 'down': + fp.write('%d' % self.LED_COLOR_OFF) + if p.singular: + return + +def getLedControl(): + return LedControl diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/utils/sonic_psu.py b/platform/broadcom/sonic-platform-modules-arista/arista/utils/sonic_psu.py new file mode 100644 index 000000000000..8adbfd077276 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/utils/sonic_psu.py @@ -0,0 +1,34 @@ +from __future__ import absolute_import + +from ..core import platform as core_platform +from .. import platforms + +try: + from sonic_psu.psu_base import PsuBase +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + + +def getPsuUtil(): + platform = core_platform.getPlatform() + inventory = platform.getInventory() + + class PsuUtil(PsuBase): + """Platform-specific PsuUtil class""" + + def get_psu_presence(self, index): + if index > inventory.getNumPsus() and index > 0: + return False + + return inventory.getPsu(index-1).getPresence() + + def get_psu_status(self, index): + if index > inventory.getNumPsus() and index > 0: + return False + + return inventory.getPsu(index-1).getStatus() + + def get_num_psus(self): + return inventory.getNumPsus() + + return PsuUtil diff --git a/platform/broadcom/sonic-platform-modules-arista/arista/utils/sonic_sfputil.py b/platform/broadcom/sonic-platform-modules-arista/arista/utils/sonic_sfputil.py new file mode 100644 index 000000000000..0cd16ceeb495 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/arista/utils/sonic_sfputil.py @@ -0,0 +1,95 @@ +import time + +from ..core import platform as core_platform +from .. import platforms + +try: + from sonic_sfp.sfputilbase import SfpUtilBase +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + + +def getSfpUtil(): + platform = core_platform.getPlatform() + inventory = platform.getInventory() + + class SfpUtil(SfpUtilBase): + """Platform-specific SfpUtil class""" + + @property + def port_start(self): + return inventory.portStart + + @property + def port_end(self): + return inventory.portEnd + + @property + def qsfp_ports(self): + return inventory.qsfpRange + + # XXX: defining the sfp_ports property currently can't be done as + # it affect the code logic of the sfputil tool by preventing + # the qsfp ports from being detected + #@property + #def sfp_ports(self): + # return inventory.sfpRange + + @property + def port_to_eeprom_mapping(self): + return inventory.getPortToEepromMapping() + + @property + def port_to_i2cbus_mapping(self): + return inventory.getPortToI2cAdapterMapping() + + def __init__(self): + SfpUtilBase.__init__(self) + + def get_presence(self, port_num): + if not self._is_valid_port(port_num): + return False + + return inventory.getXcvr(port_num).getPresence() + + def get_low_power_mode(self, port_num): + if not self._is_valid_port(port_num): + return False + + return inventory.getXcvr(port_num).getLowPowerMode() + + def set_low_power_mode(self, port_num, lpmode): + if not self._is_valid_port(port_num): + return False + + try: + return inventory.getXcvr(port_num).setLowPowerMode(lpmode) + except: + #print('failed to set low power mode for xcvr %d' % port_num) + return False + + def reset(self, port_num): + if not self._is_valid_port(port_num): + return False + + xcvr = inventory.getXcvr(port_num) + try: + if not xcvr.reset(True): + return False + except: + #print('failed to put xcvr %d in reset' % port_num) + return False + + # Sleep 1 second to allow it to settle + time.sleep(1) + + try: + if not xcvr.reset(False): + return False + except: + #print('failed to take xcvr %d out of reset' % port_num) + return False + + return True + + return SfpUtil diff --git a/platform/broadcom/sonic-platform-modules-arista/confs/arista-drivers.service b/platform/broadcom/sonic-platform-modules-arista/confs/arista-drivers.service new file mode 100644 index 000000000000..7c6551df9086 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/confs/arista-drivers.service @@ -0,0 +1,14 @@ +[Unit] +Description=Arista kernel modules init +After=local-fs.target +Before=opennsl-modules-3.16.0-5-amd64.service +ConditionKernelCommandLine=Aboot + +[Service] +Type=oneshot +ExecStart=/usr/bin/arista -l /var/log/arista.log setup --reset --background +ExecStop=/usr/bin/arista -l /var/log/arista.log clean +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-arista/confs/gardena-watchdog-stop.service b/platform/broadcom/sonic-platform-modules-arista/confs/gardena-watchdog-stop.service new file mode 100644 index 000000000000..af912ce7cee9 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/confs/gardena-watchdog-stop.service @@ -0,0 +1,18 @@ +[Unit] +Description=Disable the watchdog after boot +After=swss.service +After=opennsl-modules-3.16.0-5-amd64.service +ConditionKernelCommandLine=sid=Gardena + +[Service] +User=root +Type=oneshot +RemainAfterExit=true + +ExecStart= +ExecStart=/usr/bin/arista-gardena-watchdog --stop + +ExecStop= + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-arista/debian/changelog b/platform/broadcom/sonic-platform-modules-arista/debian/changelog new file mode 100755 index 000000000000..196c2e26a98b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/debian/changelog @@ -0,0 +1,5 @@ +sonic-platform-arista (1.0) unstable; + + * Initial release + + -- Samuel Angebault Mon, 24 Nov 2016 11:11:11 -0800 diff --git a/platform/broadcom/sonic-platform-modules-arista/debian/compat b/platform/broadcom/sonic-platform-modules-arista/debian/compat new file mode 100755 index 000000000000..ec635144f600 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/debian/compat @@ -0,0 +1 @@ +9 diff --git a/platform/broadcom/sonic-platform-modules-arista/debian/control b/platform/broadcom/sonic-platform-modules-arista/debian/control new file mode 100755 index 000000000000..ae53eea5bc81 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/debian/control @@ -0,0 +1,54 @@ +Source: sonic-platform-arista +Section: main +Priority: extra +Maintainer: Samuel Angebault +Build-Depends: + dh-python, + debhelper (>= 9.0.0), + python-all, + python(>=2.7-3~), + python-setuptools, + python3(>=3.2), + python3-setuptools, + bzip2 +Standards-Version: 1.0.0 +X-Python-Version: >= 2.7 +X-Python3-Version: >= 3.2 +XS-Python-Version: >= 2.7 +XS-Python3-Version: >= 3.2 + +Package: drivers-sonic-platform-arista +Architecture: amd64 +Depends: + ${misc:Depends}, + linux-image-3.16.0-5-amd64 +Description: Arista kernel modules for arista platform devices such as fan, led, sfp, psu + +Package: python-sonic-platform-arista +Architecture: all +Depends: + ${python:Depends}, + ${misc:Depends}, + python-smbus +Description: Arista python2 libraries + These libraries allow to work with devices such as fan, led, sfp, psu + +Package: python3-sonic-platform-arista +Architecture: all +Depends: + ${python3:Depends}, + ${misc:Depends} +Recommends: + python3-smbus +Description: Arista python3 libraries + These libraries allow to work with devices such as fan, led, sfp, psu + +Package: sonic-platform-arista +Architecture: amd64 +Depends: + ${python:Depends}, + ${misc:Depends}, + python-sonic-platform-arista, + python(>=2.7), + i2c-tools +Description: Miscellaneous Arista scripts and tools diff --git a/platform/broadcom/sonic-platform-modules-arista/debian/copyright b/platform/broadcom/sonic-platform-modules-arista/debian/copyright new file mode 100644 index 000000000000..ebdb940a0c98 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/debian/copyright @@ -0,0 +1,1016 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + + + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/platform/broadcom/sonic-platform-modules-arista/debian/files b/platform/broadcom/sonic-platform-modules-arista/debian/files new file mode 100644 index 000000000000..2ad60d05f0f1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/debian/files @@ -0,0 +1,4 @@ +drivers-sonic-platform-arista_1.0_amd64.deb main extra +python-sonic-platform-arista_1.0_all.deb main extra +python3-sonic-platform-arista_1.0_all.deb main extra +sonic-platform-arista_1.0_amd64.deb main extra diff --git a/platform/broadcom/sonic-platform-modules-arista/debian/rules b/platform/broadcom/sonic-platform-modules-arista/debian/rules new file mode 100755 index 000000000000..e12a638d8742 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/debian/rules @@ -0,0 +1,54 @@ +#!/usr/bin/make -f + +include /usr/share/dpkg/pkg-info.mk + +#export DH_VERBOSE = 1 + +export INSTALL_MOD_DIR:=extra + +SCRIPT_FILES := reset +BIN_FILES := arista boot-eos arista-gardena-watchdog +SERVICE_FILES := gardena-watchdog-stop.service + +KVERSION ?= $(shell uname -r) +KERNEL_SRC ?= /lib/modules/$(KVERSION)/build +KERNEL_DST := /lib/modules/$(KVERSION) +BASE_DIR := $(shell pwd) +MODULE_SRC := $(BASE_DIR)/src +TEST_DIR := $(BASE_DIR)/tests +SCRIPT_SRC := $(addprefix $(BASE_DIR)/utils/,$(SCRIPT_FILES)) +BIN_SRC := $(addprefix $(BASE_DIR)/utils/,$(BIN_FILES)) +SERVICE_SRC := $(addprefix $(BASE_DIR)/confs/,$(SERVICE_FILES)) + +%: + dh $@ --with python2,python3,systemd --buildsystem=pybuild + +override_dh_auto_build: + $(MAKE) -C $(KERNEL_SRC) M=$(MODULE_SRC) + python2 setup.py build + +override_dh_auto_install: + dh_installdirs -p$(DEB_SOURCE) usr/bin + cp $(BIN_SRC) debian/$(DEB_SOURCE)/usr/bin + dh_installdirs -p$(DEB_SOURCE) usr/share/arista + cp $(SCRIPT_SRC) debian/$(DEB_SOURCE)/usr/share/arista + dh_installdirs -p$(DEB_SOURCE) lib/systemd/system + cp $(SERVICE_SRC) debian/$(DEB_SOURCE)/lib/systemd/system + dh_installdirs -pdrivers-$(DEB_SOURCE) $(KERNEL_DST)/$(INSTALL_MOD_DIR) + cp $(MODULE_SRC)/*.ko debian/drivers-$(DEB_SOURCE)/$(KERNEL_DST)/$(INSTALL_MOD_DIR) + python2 setup.py install --root=$(BASE_DIR)/debian/python-$(DEB_SOURCE) --install-layout=deb + python3 setup.py install --root=$(BASE_DIR)/debian/python3-$(DEB_SOURCE) --install-layout=deb + +override_dh_clean: + dh_clean + $(RM) $(MODULE_SRC)/*.o $(MODULE_SRC)/*.ko $(MODULE_SRC)/*.mod.c $(MODULE_SRC)/.*.cmd + $(RM) $(MODULE_SRC)/Module.markers $(MODULE_SRC)/Module.symvers $(MODULE_SRC)/modules.order + $(RM) -r $(MODULE_SRC)/.tmp_versions + $(RM) -r $(BASE_DIR)/*.egg-info $(BASE_DIR)/build + +override_dh_auto_test: + PYTHON=python2 PYTHONPATH=$(BASE_DIR) $(TEST_DIR)/all-platforms.sh + PYTHON=python3 PYTHONPATH=$(BASE_DIR) $(TEST_DIR)/all-platforms.sh + +print-%: + @echo $($*) diff --git a/platform/broadcom/sonic-platform-modules-arista/debian/sonic-platform-arista.init b/platform/broadcom/sonic-platform-modules-arista/debian/sonic-platform-arista.init new file mode 100755 index 000000000000..7042efadabc3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/debian/sonic-platform-arista.init @@ -0,0 +1,47 @@ +#!/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 Arista platform +### END INIT INFO + +drivers_start() { + echo "Loading arista drivers..." + depmod -a + if grep -q sid=Gardena /proc/cmdline; then + timeout=600 + echo "Enabling watchdog for $timeout seconds" + /usr/bin/arista-gardena-watchdog -o $timeout + fi + /usr/bin/arista --syslog -l /var/log/arista.log setup --reset --background + echo "done." +} +drivers_stop() { + echo "Unloading arista drivers..." + /usr/bin/arista --syslog -l /var/log/arista.log clean + echo "done." +} + +case "$1" in + start) drivers_start ;; + stop) drivers_stop ;; + force-reload) echo "Not supported";; + restart) + drivers_stop + drivers_start + ;; + + *) + echo "Usage: $0 {start|stop}" + exit 1 + ;; +esac + +exit 0 + diff --git a/platform/broadcom/sonic-platform-modules-arista/setup.py b/platform/broadcom/sonic-platform-modules-arista/setup.py new file mode 100755 index 000000000000..ea5c86513fde --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/setup.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +from setuptools import setup +import os + +setup( + name='platform-arista', + version='%s' % os.environ.get('ARISTA_PLATFORM_MODULE_VERSION', '1.0'), + description='Module to initialize arista platforms', + packages=[ + 'arista', + 'arista.core', + 'arista.components', + 'arista.platforms', + 'arista.utils', + ], +) + diff --git a/platform/broadcom/sonic-platform-modules-arista/src/Makefile b/platform/broadcom/sonic-platform-modules-arista/src/Makefile new file mode 100644 index 000000000000..378fec02c12e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/src/Makefile @@ -0,0 +1,9 @@ +ccflags-y := -Werror + +obj-m += scd.o +obj-m += scd-hwmon.o +obj-m += sonic-support-driver.o +obj-m += crow-fan-driver.o +obj-m += raven-fan-driver.o +obj-m += rook-led-driver.o +obj-m += rook-fan-cpld.o diff --git a/platform/broadcom/sonic-platform-modules-arista/src/crow-fan-driver.c b/platform/broadcom/sonic-platform-modules-arista/src/crow-fan-driver.c new file mode 100644 index 000000000000..851a26f8d95b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/src/crow-fan-driver.c @@ -0,0 +1,505 @@ +/* Copyright (c) 2016 Arista Networks, Inc. + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "crow-cpld-fans" + +#define NUM_FANS 4 +#define LED_NAME_MAX_SZ 20 + +#define TACH1LOWREG 0 +#define TACH1HIGHREG 1 +#define TACH2LOWREG 2 +#define TACH2HIGHREG 3 +#define TACH3LOWREG 4 +#define TACH3HIGHREG 5 +#define TACH4LOWREG 6 +#define TACH4HIGHREG 7 + +#define FAN1PWMREG 0x10 +#define FAN1IDREG 0x18 +#define FAN2PWMREG 0x11 +#define FAN2IDREG 0x19 +#define FAN3PWMREG 0x12 +#define FAN3IDREG 0x1A +#define FAN4PWMREG 0x13 +#define FAN4IDREG 0x1B + +#define FANPRESENTREG 0x21 +#define FANGREENLEDREG 0x24 +#define FANREDLEDREG 0x25 +#define CROWCPLDREVREG 0x40 +#define SCRATCHREG 0x41 + +#define FAN_LED_OFF 0 +#define FAN_LED_GREEN 1 +#define FAN_LED_RED 2 +#define FAN_LED_YELLOW 3 + +struct crow_led { + char name[LED_NAME_MAX_SZ]; + struct led_classdev cdev; + int fan_index; +}; + +struct crow_cpld_data { + struct i2c_client *client; + struct crow_led leds[NUM_FANS]; +}; + +static s32 read_cpld(struct device *dev, u8 reg, char *buf) +{ + int err; + struct crow_cpld_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + err = i2c_smbus_read_byte_data(client, reg); + if (err < 0) { + dev_err(dev, "failed to read reg %d of with error code: %d\n", reg, err); + return err; + } + + *buf = (err & 0xFF); + return 0; +} + +static s32 write_cpld(struct device *dev, u8 reg, u8 byte) +{ + int err; + struct crow_cpld_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + err = i2c_smbus_write_byte_data(client, reg, byte); + if (err) { + dev_err(dev, "failed to write %02x in reg %02x of with error code: %d\n", + byte, reg, err); + } + + return err; +} + +static s32 read_cpld_buf(struct device *dev, u8 reg, char *buf) +{ + s32 status; + u8 data; + + status = read_cpld(dev, reg, &data); + if (status) { + return status; + } + + return sprintf(buf, "%hhu\n", data); +} + +static s32 write_cpld_buf(struct device *dev, u8 reg, const char *buf) +{ + u8 data; + s32 status; + + if (sscanf(buf, "%hhu", &data) != 1) { + return -EINVAL; + } + + status = write_cpld(dev, reg, data); + + return status; +} + +static s32 read_led_color(struct device *dev, int index, u8 *color) +{ + s32 err; + u8 data; + unsigned char read_value_g = 0; + unsigned char read_value_r = 0; + + err = read_cpld(dev, FANGREENLEDREG, &data); + if (err) + return err; + read_value_g = (data >> index) & 0x01; + + err = read_cpld(dev, FANREDLEDREG, &data); + if (err) + return err; + read_value_r = (data >> index) & 0x01; + + *color = FAN_LED_OFF; + if((!read_value_g) && read_value_r) { + *color = FAN_LED_GREEN; + } else if (read_value_g && (!read_value_r)) { + *color = FAN_LED_RED; + } else if((!read_value_g) && (!read_value_r)) { + *color = FAN_LED_YELLOW; + } + + return 0; +} + +static s32 read_led_color_buf(struct device *dev, char *buf, int index) +{ + int err; + u8 val; + + err = read_led_color(dev, index, &val); + if (err) + return err; + + return sprintf(buf, "%d\n", val); +} + +static s32 write_led_color(struct device *dev, u8 value, int index) +{ + s32 status; + u8 red_value; + u8 green_value; + u8 green_led; + u8 red_led; + + if (value > 3) + return -EINVAL; + + switch (value) { + case FAN_LED_GREEN: + green_led = 1; + red_led = 0; + break; + case FAN_LED_RED: + green_led = 0; + red_led = 1; + break; + case FAN_LED_YELLOW: + green_led = 1; + red_led = 1; + break; + case FAN_LED_OFF: + default: + green_led = 0; + red_led = 0; + break; + } + + status = read_cpld(dev, FANGREENLEDREG, &green_value); + if (status) { + return status; + } + + status = read_cpld(dev, FANREDLEDREG, &red_value); + if (status) { + return status; + } + + if (green_led) { + green_value &= ~(1u << index); + } else { + green_value |= (1u << index); + } + + if (red_led) { + red_value &= ~(1u << index); + } else { + red_value |= (1u << index); + } + + status = write_cpld(dev, FANGREENLEDREG, green_value); + status |= write_cpld(dev, FANREDLEDREG, red_value); + + return status; +} + +static s32 write_led_color_buf(struct device *dev, const char *buf, int index) +{ + u8 value; + + if (sscanf(buf, "%hhu", &value) != 1) { + return -EINVAL; + } + + return write_led_color(dev, value, index); +} + +static s32 read_tach(struct device *dev, u8 tachHigh, u8 tachLow, u32 *speed) +{ + s32 status; + u8 dataHigh; + u8 dataLow; + u32 tachData; + + status = read_cpld(dev, tachHigh, &dataHigh); + status |= read_cpld(dev, tachLow, &dataLow); + if (status) { + return status; + } + + tachData = (dataHigh << 8) + dataLow; + if (!tachData) { + tachData = 1; + } + *speed = 6000000 / tachData; + *speed = *speed / 2; + + return 0; +} + +static s32 read_tach_buf(struct device *dev, u8 tachHigh, u8 tachLow, + char *buf) +{ + u32 speed; + int err; + + err = read_tach(dev, tachHigh, tachLow, &speed); + if (err) + return err; + + return sprintf(buf, "%d\n", speed); +} + +static s32 read_fan_present(struct device *dev, int index, u8 *present) +{ + s32 status; + u8 data; + + *present = 0; + status = read_cpld(dev, FANPRESENTREG, &data); + if (status) { + return status; + } + + *present = ~(data >> index) & 0x01; + return 0; +} + +static s32 read_fan_present_buf(struct device *dev, char *buf, int index) +{ + int err; + u8 present; + + err = read_fan_present(dev, index, &present); + if (err) + return err; + + return sprintf(buf, "%d\n", present); +} + +#define GENERIC_FAN_READ(_name, _dev, _reg) \ +static ssize_t fan_##_name##_##_dev##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return read_cpld_buf(dev, _reg, buf); \ +} \ + +#define GENERIC_FAN_WRITE(_name, _dev, _reg) \ +static ssize_t fan_##_name##_##_dev##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + write_cpld_buf(dev, _reg, buf); \ + return count; \ +} \ + +#define GENERIC_LED(_name) \ +static ssize_t fan_##_name##_led_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + return read_led_color_buf(dev, buf, _name-1); \ +} \ +static ssize_t fan_##_name##_led_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + write_led_color_buf(dev, buf, _name-1); \ + return count; \ +} \ +DEVICE_ATTR(fan##_name##_led, S_IRUGO|S_IWGRP|S_IWUSR, \ + fan_##_name##_led_show, fan_##_name##_led_store); \ + + +#define FAN_DEVICE_ATTR(_name) \ +static ssize_t tach_##_name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + return read_tach_buf(dev, TACH##_name##HIGHREG, TACH##_name##LOWREG, buf); \ +} \ +DEVICE_ATTR(fan##_name##_input, S_IRUGO, tach_##_name##_show, NULL); \ + \ +GENERIC_FAN_READ(_name, id, FAN##_name##IDREG); \ +DEVICE_ATTR(fan##_name##_id, S_IRUGO, fan_##_name##_id_show, NULL); \ + \ +GENERIC_FAN_READ(_name, pwm, FAN##_name##PWMREG); \ +GENERIC_FAN_WRITE(_name, pwm, FAN##_name##PWMREG); \ +DEVICE_ATTR(pwm##_name, S_IRUGO|S_IWGRP|S_IWUSR, \ + fan_##_name##_pwm_show, fan_##_name##_pwm_store); \ + \ +static ssize_t fan_##_name##_present_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + return read_fan_present_buf(dev, buf, _name-1); \ +} \ +DEVICE_ATTR(fan##_name##_present, S_IRUGO, fan_##_name##_present_show, NULL); \ + \ +GENERIC_LED(_name) \ + + +static ssize_t crow_cpld_rev_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return read_cpld_buf(dev, CROWCPLDREVREG, buf); +} + +DEVICE_ATTR(crow_cpld_rev, S_IRUGO, crow_cpld_rev_show, NULL); + +FAN_DEVICE_ATTR(1); +FAN_DEVICE_ATTR(2); +FAN_DEVICE_ATTR(3); +FAN_DEVICE_ATTR(4); + +#define FANATTR(_name) \ + &dev_attr_pwm##_name.attr, \ + &dev_attr_fan##_name##_id.attr, \ + &dev_attr_fan##_name##_input.attr, \ + &dev_attr_fan##_name##_present.attr, \ + &dev_attr_fan##_name##_led.attr, \ + + +static struct attribute *fan_attrs[] = { + FANATTR(1) + FANATTR(2) + FANATTR(3) + FANATTR(4) + &dev_attr_crow_cpld_rev.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(fan); + +static void brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct crow_led *pled = container_of(led_cdev, struct crow_led, cdev); + struct device *dev = led_cdev->dev->parent; + + write_led_color(dev, value, pled->fan_index); +} + +static enum led_brightness brightness_get(struct led_classdev *led_cdev) +{ + struct crow_led *pled = container_of(led_cdev, struct crow_led, cdev); + struct device *dev = led_cdev->dev->parent; + u8 val; + int err; + + err = read_led_color(dev, pled->fan_index, &val); + if (err) + return 0; + + return val; +} + +static void leds_unregister(struct crow_cpld_data *data, int num_leds) +{ + int i = 0; + + for (i = 0; i < num_leds; i++) + led_classdev_unregister(&data->leds[i].cdev); +} + +static int leds_init(struct crow_led *leds, struct i2c_client *client) +{ + int i; + int err; + struct crow_cpld_data *data = i2c_get_clientdata(client); + + for (i = 0; i < NUM_FANS; i++) { + leds[i].fan_index = i; + leds[i].cdev.brightness_set = brightness_set; + leds[i].cdev.brightness_get = brightness_get; + scnprintf(leds[i].name, LED_NAME_MAX_SZ, "fan%d", leds[i].fan_index + 1); + leds[i].cdev.name = leds[i].name; + } + + // fan leds initialized to green because no fan fault reg on crow + for (i = 0 ; i < NUM_FANS; i++) { + err = led_classdev_register(&client->dev, &leds[i].cdev); + err |= write_led_color(&client->dev, FAN_LED_GREEN, i); + if (err) { + leds_unregister(data, i); + return err; + } + } + + return 0; +} + +static int crow_cpld_remove(struct i2c_client *client) +{ + struct crow_cpld_data *data = i2c_get_clientdata(client); + + leds_unregister(data, NUM_FANS); + + return 0; +} + +static int crow_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct crow_cpld_data *data; + data = devm_kzalloc(dev, sizeof(struct crow_cpld_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->client = client; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, fan_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + err = leds_init(data->leds, client); + if (err) + return err; + + return 0; +} + +static const struct i2c_device_id crow_cpld_id[] = { + { "crow_cpld", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, crow_cpld_id); + +static struct i2c_driver crow_cpld_driver = { + .driver = { + .name = DRIVER_NAME + }, + .id_table = crow_cpld_id, + .probe = crow_cpld_probe, + .remove = crow_cpld_remove, +}; + +module_i2c_driver(crow_cpld_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arista Networks"); +MODULE_DESCRIPTION("Crow Fan driver"); diff --git a/platform/broadcom/sonic-platform-modules-arista/src/gpio-kversfix.h b/platform/broadcom/sonic-platform-modules-arista/src/gpio-kversfix.h new file mode 100644 index 000000000000..7abc7de39f6e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/src/gpio-kversfix.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2017 Arista Networks, Inc. + * + * 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, see . + * + */ + +#ifndef _LINUX_DRIVER_GPIO_FIX_H_ +#define _LINUX_DRIVER_GPIO_FIX_H_ + +#include +#include + +/* + * The following snippet of code is a workaround to support kernel prior to 3.18 + * These previous kernel doesn't benefit of the gpio subsystem refactor that exports + * more functions. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) +#include +#include + +#define gpiochip_free_own_desc gpiochip_free_desc_hack +void gpiochip_free_desc_hack(struct gpio_desc *desc) +{ + // this call decrease the refcount of the module which means that it is an issue + // if called outside of the module_exit + gpio_free(desc_to_gpio(desc)); + try_module_get(THIS_MODULE); +} + +#define gpiochip_request_own_desc gpiochip_request_desc_hack +struct gpio_desc *gpiochip_request_desc_hack(struct gpio_chip *chip, + u16 hwnum, const char *label) +{ + struct gpio_desc *desc = gpiochip_get_desc(chip, hwnum); + int err; + + if (IS_ERR(desc)) { + pr_err("gpio: failed to get GPIO descriptor\n"); + return desc; + } + + err = gpio_request(desc_to_gpio(desc), label); + if (err < 0) { + pr_err("gpio: failed to request GPIO"); + return ERR_PTR(err); + } + + // gpio_request increase the refcount on the module + // Since the module asking for its own gpio, the refcount shouldn't be + // increased. Given that this is the only exported symbol available this is the + // is the easiest way to handle this without adding a kernel patch + module_put(chip->owner); + + return desc; +} +#endif /* LINUX_VERSION < 3.18.0 */ + +#endif /* !_LINUX_DRIVER_GPIO_FIX_H_ */ diff --git a/platform/broadcom/sonic-platform-modules-arista/src/raven-fan-driver.c b/platform/broadcom/sonic-platform-modules-arista/src/raven-fan-driver.c new file mode 100644 index 000000000000..60716a8c8d71 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/src/raven-fan-driver.c @@ -0,0 +1,527 @@ +/* Copyright (c) 2016 Arista Networks, Inc. + * + * 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, see . + * + * The AMD SB800 Register Reference Guide details the behavior of the + * SB800 accesses: http://support.amd.com/TechDocs/45482.pdf + */ + +#include +#include +#include +#include +#include +#include "gpio-kversfix.h" + +#define DRIVER_NAME "sb800-fans" + +#define LED_NAME_MAX_SZ 20 +#define NUM_FANS 4 + +#define SB800_BASE 0xfed80000 +#define SB800_GPIO_BASE (SB800_BASE + 0x0100) +#define SB800_GPIO_SIZE 0xff +#define SB800_PM2_BASE (SB800_BASE + 0x0400) +#define SB800_PM2_SIZE 0xff +#define SB800_IOSIZE 4 + +#define FAN_ID_BASE_ADDR 0xCB +#define FAN_ID_ADDR_OFFSET 6 +#define NUM_FAN_ID_PINS 3 + +#define FAN1_PRESENT_ADDR 206 +#define FAN2_PRESENT_ADDR 212 +#define FAN3_PRESENT_ADDR 220 +#define FAN4_PRESENT_ADDR 224 + +#define GREEN_RED_LED_ADDR_OFFSET 1 +#define FAN1_GREEN_LED_ADDR 207 +#define FAN2_GREEN_LED_ADDR 213 +#define FAN3_GREEN_LED_ADDR 218 +#define FAN4_GREEN_LED_ADDR 225 + +#define FAN_LED_OFF 0 +#define FAN_LED_GREEN 1 +#define FAN_LED_RED 2 +#define FAN_LED_YELLOW 3 + +#define LED_ON_OFF_REG_OFFSET (1 << 6) +#define LED_DIR_REG_OFFSET (1 << 5) + +#define FAN_TACH_BASE_ADDR 0x69 +#define FAN_TACH_ADDR_OFFSET 0x05 +#define FAN_TACH_LOW_HI_ADDR_OFFSET 0x1 + +#define FAN_CTRL_BASE_ADDR 1 +#define FAN_FREQ_BASE_ADDR 2 +#define FAN_DUTY_BASE_ADDR 3 +#define FAN_CTRL_ADDR_OFFSET 0x10 + +#define FAN_DETECT_CTRL_BASE_ADDR 0x66 +#define FAN_DETECT_ADDR_OFFSET 0x05 + +#define FAN_PWM_BASE_ADDR 3 +#define FAN_PWM_ADDR_OFFSET 0x10 + +struct raven_led { + char name[LED_NAME_MAX_SZ]; + struct led_classdev cdev; + int fan_index; +}; + +struct raven_pdata { + struct device *hwmon_dev; + struct raven_led leds[NUM_FANS]; + u8 *gpio_base; + u8 *pm2_base; +}; + +static struct platform_device *sb800_pdev = 0; + +unsigned long const green_led_addrs[NUM_FANS] = {FAN1_GREEN_LED_ADDR, + FAN2_GREEN_LED_ADDR, + FAN3_GREEN_LED_ADDR, + FAN4_GREEN_LED_ADDR}; + +static ssize_t show_fan_present(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct raven_pdata *pdata = dev_get_drvdata(dev->parent); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + unsigned long const fan_present_addrs[] = {FAN1_PRESENT_ADDR, FAN2_PRESENT_ADDR, + FAN3_PRESENT_ADDR, FAN4_PRESENT_ADDR}; + u32 num_fan = sensor_attr->index - 1; + u8 *fan_present_reg = pdata->gpio_base + fan_present_addrs[num_fan]; + u32 fan_present_val = ((~ioread8(fan_present_reg)) >> 7) & 0x1; + return scnprintf(buf, 5, "%u\n", fan_present_val); +} + +static ssize_t show_fan_id(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct raven_pdata *pdata = dev_get_drvdata(dev->parent); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + u32 fan_id = sensor_attr->index - 1; + int num_id; + u32 id_vals[NUM_FAN_ID_PINS]; + u8 *reg_id = pdata->gpio_base + FAN_ID_BASE_ADDR + FAN_ID_ADDR_OFFSET * fan_id; + for(num_id = 0; num_id < NUM_FAN_ID_PINS; num_id++) { + reg_id += num_id; + id_vals[num_id] = (ioread8(reg_id) >> 7) & 0x1; + } + return scnprintf(buf, 12, "%u %u %u\n", id_vals[2], id_vals[1], id_vals[0]); +} + +static int read_led(struct raven_pdata *pdata, int fan_id, u8 *value) +{ + unsigned long const green_led_addr = green_led_addrs[fan_id]; + u8 *reg_g = pdata->gpio_base + green_led_addr; + u8 *reg_r = reg_g + GREEN_RED_LED_ADDR_OFFSET; + u32 val_g = ioread8(reg_g); + u32 val_r = ioread8(reg_r); + + *value = FAN_LED_OFF; + + if (!(val_g & LED_ON_OFF_REG_OFFSET) && (val_r & LED_ON_OFF_REG_OFFSET)) { + *value = FAN_LED_GREEN; + } + else if ((val_g & LED_ON_OFF_REG_OFFSET) && !(val_r & LED_ON_OFF_REG_OFFSET)) { + *value = FAN_LED_RED; + } + else if (!(val_g & LED_ON_OFF_REG_OFFSET) && !(val_r & LED_ON_OFF_REG_OFFSET)) { + *value = FAN_LED_YELLOW; + } + + return 0; +} + +static ssize_t show_led(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct raven_pdata *pdata = dev_get_drvdata(dev->parent); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int err; + u8 color; + + err = read_led(pdata, sensor_attr->index - 1, &color); + if (err) + return err; + + return scnprintf(buf, 16, "%u\n", color); +} + +static int write_led(struct raven_pdata *pdata, u8 val, int index) +{ + unsigned long const green_led_addr = green_led_addrs[index]; + u8 *reg_g = pdata->gpio_base + green_led_addr; + u8 *reg_r = reg_g + GREEN_RED_LED_ADDR_OFFSET; + u32 val_g = ioread8(reg_g); + u32 val_r = ioread8(reg_r); + + if (val > 3) + return -EINVAL; + + // Enable output + val_g &= ~LED_DIR_REG_OFFSET; + val_r &= ~LED_DIR_REG_OFFSET; + + switch (val) { + case FAN_LED_OFF: + val_g |= LED_ON_OFF_REG_OFFSET; + val_r |= LED_ON_OFF_REG_OFFSET; + break; + case FAN_LED_RED: + val_r &= ~LED_ON_OFF_REG_OFFSET; + val_g |= LED_ON_OFF_REG_OFFSET; + break; + case FAN_LED_YELLOW: + val_g &= ~LED_ON_OFF_REG_OFFSET; + val_r &= ~LED_ON_OFF_REG_OFFSET; + break; + default: // Green + val_g &= ~LED_ON_OFF_REG_OFFSET; + val_r |= LED_ON_OFF_REG_OFFSET; + break; + } + iowrite8(val_g, reg_g); + iowrite8(val_r, reg_r); + + return 0; +} + +static ssize_t store_led(struct device * dev, struct device_attribute * attr, + const char * buf, size_t count) +{ + unsigned long value; + int err; + struct raven_pdata *pdata = dev_get_drvdata(dev->parent); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + u32 fan_id = sensor_attr->index - 1; + + err = kstrtoul(buf, 10, &value); + if (err) { + return err; + } + + err = write_led(pdata, value, fan_id); + if (err) + return err; + + return count; +} + +static void brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct raven_led *pled = container_of(led_cdev, struct raven_led, + cdev); + struct raven_pdata *pdata = dev_get_drvdata(led_cdev->dev->parent); + + write_led(pdata, value, pled->fan_index); +} + +static enum led_brightness brightness_get(struct led_classdev *led_cdev) +{ + int err; + struct raven_led *pled = container_of(led_cdev, struct raven_led, + cdev); + struct raven_pdata *pdata = dev_get_drvdata(led_cdev->dev->parent); + u8 value; + + err = read_led(pdata, pled->fan_index, &value); + if (err) + return 0; + + return value; +} + +static void leds_unregister(struct raven_pdata *pdata, int num_leds) +{ + int i; + + for (i = 0; i < num_leds; i++) + led_classdev_unregister(&pdata->leds[i].cdev); +} + +static int leds_register(struct device* dev, struct raven_pdata *pdata) +{ + int i; + int err; + struct raven_led *led; + + for (i = 0; i < NUM_FANS; i++) { + led = &pdata->leds[i]; + led->fan_index = i; + led->cdev.brightness_set = brightness_set; + led->cdev.brightness_get = brightness_get; + scnprintf(led->name, LED_NAME_MAX_SZ, "fan%d", led->fan_index + 1); + led->cdev.name = led->name; + err = led_classdev_register(dev, &led->cdev); + if (err) { + leds_unregister(pdata, i); + return err; + } + } + + return 0; +} + +static ssize_t show_fan_input(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u32 tach = 0; + u32 tach_lo; + u32 tach_lo_1; + u32 tach_hi; + u32 tach_hi_1; + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + u32 fan_id = sensor_attr->index - 1; + struct raven_pdata *pdata = dev_get_drvdata(dev->parent); + unsigned long tach_lo_addr = FAN_TACH_BASE_ADDR + + (fan_id * FAN_TACH_ADDR_OFFSET ); + u8 *tach_lo_reg = pdata->pm2_base + tach_lo_addr; + u8 *tach_hi_reg = tach_lo_reg + FAN_TACH_LOW_HI_ADDR_OFFSET; + tach_lo = ioread8(tach_lo_reg); + tach_hi = ioread8(tach_hi_reg); + tach_lo_1 = ioread8(tach_lo_reg); + tach_hi_1 = ioread8(tach_hi_reg); + if (tach_lo_1 == tach_lo) { + tach = (tach_hi << 8) + tach_lo; + } else { + tach = (tach_hi_1 << 8) + tach_lo_1; + } + tach = (22700 * 60) / ((tach ?: 1) * 2); + return scnprintf(buf, 12, "%u\n", tach); +} + +static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct raven_pdata *pdata = dev_get_drvdata(dev->parent); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + u32 fan_id = sensor_attr->index - 1; + u8 * reg = pdata->pm2_base + FAN_PWM_BASE_ADDR + (fan_id * FAN_PWM_ADDR_OFFSET); + u32 pwm = ioread8(reg); + return scnprintf(buf, 5, "%u\n", pwm); +} + +static ssize_t store_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct raven_pdata *pdata = dev_get_drvdata(dev->parent); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + unsigned long pwm; + int ret = 0; + u32 fan_id = sensor_attr->index - 1; + u8 * reg = pdata->pm2_base + FAN_PWM_BASE_ADDR + (fan_id * FAN_PWM_ADDR_OFFSET); + ret = kstrtoul(buf, 10, &pwm); + if (ret) { + return ret; + } + iowrite8(pwm & 0xff, reg); + return count; +} + +#define FAN_DEVICE_ATTR(_numfan) \ +static SENSOR_DEVICE_ATTR(fan##_numfan##_input, S_IRUGO, \ + show_fan_input, NULL, _numfan); \ +static SENSOR_DEVICE_ATTR(pwm##_numfan, S_IRUGO|S_IWUSR|S_IWGRP, \ + show_pwm, store_pwm, _numfan); \ +static SENSOR_DEVICE_ATTR(fan##_numfan##_present, S_IRUGO, \ + show_fan_present, NULL, _numfan); \ +static SENSOR_DEVICE_ATTR(fan##_numfan##_id, S_IRUGO, \ + show_fan_id, NULL, _numfan); \ +static SENSOR_DEVICE_ATTR(fan##_numfan##_led, S_IRUGO|S_IWUSR|S_IWGRP, \ + show_led, store_led, _numfan); + +FAN_DEVICE_ATTR(1); +FAN_DEVICE_ATTR(2); +FAN_DEVICE_ATTR(3); +FAN_DEVICE_ATTR(4); + +static struct attribute *fan_attrs[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_fan1_led.dev_attr.attr, + &sensor_dev_attr_fan1_present.dev_attr.attr, + &sensor_dev_attr_fan1_id.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_fan2_led.dev_attr.attr, + &sensor_dev_attr_fan2_present.dev_attr.attr, + &sensor_dev_attr_fan2_id.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_fan3_led.dev_attr.attr, + &sensor_dev_attr_fan3_present.dev_attr.attr, + &sensor_dev_attr_fan3_id.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_pwm4.dev_attr.attr, + &sensor_dev_attr_fan4_led.dev_attr.attr, + &sensor_dev_attr_fan4_present.dev_attr.attr, + &sensor_dev_attr_fan4_id.dev_attr.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(fan); + +static void set_led_init_state(struct device * dev) +{ + int num_fan; + u8 *reg_g = NULL; + u8 *reg_r = NULL; + unsigned long green_led_addr; + u8 val_g, val_r; + struct raven_pdata *pdata = dev_get_drvdata(dev); + + for(num_fan = 0; num_fan < NUM_FANS; num_fan++) { + green_led_addr = green_led_addrs[num_fan]; + reg_g = pdata->gpio_base + green_led_addr; + reg_r = reg_g + GREEN_RED_LED_ADDR_OFFSET; + val_g = ioread8(reg_g); + val_r = ioread8(reg_r); + val_g &= ~LED_DIR_REG_OFFSET; + val_r &= ~LED_DIR_REG_OFFSET; + val_g &= ~LED_ON_OFF_REG_OFFSET; // initialize leds to green + val_r |= LED_ON_OFF_REG_OFFSET; + iowrite8(val_g, reg_g); + iowrite8(val_r, reg_r); + } +} + +static void set_fan_init_state(struct device * dev) +{ + int num_fan; + struct raven_pdata *pdata = dev_get_drvdata(dev); + u8 *reg = pdata->pm2_base; + unsigned long fan_ctrl_offset; + for (num_fan = 0; num_fan < NUM_FANS; num_fan++) { + fan_ctrl_offset = FAN_CTRL_ADDR_OFFSET * num_fan; + iowrite8(0x06, reg + fan_ctrl_offset); /*FanInputControl*/ + iowrite8(0x04, reg + FAN_CTRL_BASE_ADDR + fan_ctrl_offset); + iowrite8(0x01, reg + FAN_FREQ_BASE_ADDR + fan_ctrl_offset); + iowrite8(0xff, reg + FAN_DUTY_BASE_ADDR + fan_ctrl_offset); + + iowrite8(0x01, reg + FAN_DETECT_CTRL_BASE_ADDR + (FAN_DETECT_ADDR_OFFSET * + num_fan)); + } +} + +static int sb_fan_remove(struct platform_device *pdev) +{ + int err = 0; + struct raven_pdata *pdata = platform_get_drvdata(pdev); + + leds_unregister(pdata, NUM_FANS); + + iounmap(pdata->gpio_base); + iounmap(pdata->pm2_base); + release_mem_region(SB800_PM2_BASE, SB800_PM2_SIZE); + release_mem_region(SB800_GPIO_BASE, SB800_GPIO_SIZE); + hwmon_device_unregister(pdata->hwmon_dev); + return err; +} + +static s32 sb_fan_probe(struct platform_device *pdev) +{ + int ret = 0; + int err; + struct raven_pdata *pdata = devm_kzalloc(&pdev->dev, sizeof(struct raven_pdata), + GFP_KERNEL); + + if (!request_mem_region(SB800_PM2_BASE, SB800_PM2_SIZE, "SB800_PM2")) { + dev_err(&pdev->dev, "failed request_mem_region in SB fan initialization\n"); + ret = -EBUSY; + goto fail_request_pm_region; + } + pdata->pm2_base = ioremap(SB800_PM2_BASE, SB800_PM2_SIZE ); + + if (!request_mem_region(SB800_GPIO_BASE, SB800_GPIO_SIZE, "SB800_GPIO")) { + dev_err(&pdev->dev, "Failed request_mem_region in SB GPIO initialization"); + ret = -EBUSY; + goto fail_request_gpio_region; + } + pdata->gpio_base = ioremap(SB800_GPIO_BASE, SB800_GPIO_SIZE); + + pdata->hwmon_dev = hwmon_device_register_with_groups(&pdev->dev, "fans", NULL, + fan_groups); + if (IS_ERR(pdata->hwmon_dev)) { + dev_err(&pdev->dev, "failed to create hwmon sysfs entries\n"); + ret = PTR_ERR(pdata->hwmon_dev); + goto fail_hwmon_register; + } + platform_set_drvdata(pdev, pdata); + set_led_init_state(&pdev->dev); + set_fan_init_state(&pdev->dev); + + err = leds_register(&pdev->dev, pdata); + if (err) { + ret = err; + goto fail_hwmon_register; + } + + return ret; + +fail_hwmon_register: + release_mem_region(SB800_GPIO_BASE, SB800_GPIO_SIZE); + iounmap(pdata->gpio_base); +fail_request_gpio_region: + release_mem_region(SB800_PM2_BASE, SB800_PM2_SIZE); + iounmap(pdata->pm2_base); +fail_request_pm_region: + return ret; +} + +static int __init sb_fan_init(void) +{ + int err; + struct platform_device *pdev = NULL; + + pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); + + if (IS_ERR(pdev)) { + printk(KERN_ERR "failed to register " DRIVER_NAME); + return PTR_ERR(pdev); + } + + err = sb_fan_probe(pdev); + + if (err) { + dev_err(&pdev->dev, "failed to init device "); + platform_device_unregister(pdev); + return err; + } + + sb800_pdev = pdev; + + return err; +} + +static void __exit sb_fan_exit(void) +{ + if (!sb800_pdev) { + return; + } + + sb_fan_remove(sb800_pdev); + platform_device_unregister(sb800_pdev); + + sb800_pdev = 0; +} + +module_init(sb_fan_init); +module_exit(sb_fan_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arista Networks"); +MODULE_DESCRIPTION("Raven Fan Driver"); diff --git a/platform/broadcom/sonic-platform-modules-arista/src/rook-fan-cpld.c b/platform/broadcom/sonic-platform-modules-arista/src/rook-fan-cpld.c new file mode 100644 index 000000000000..5a6933bdac4f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/src/rook-fan-cpld.c @@ -0,0 +1,963 @@ +/* Copyright (c) 2017 Arista Networks, Inc. + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "rook-fan-cpld" + +#define LED_NAME_MAX_SZ 20 +#define MAX_FAN_COUNT 8 + +#define MINOR_VERSION_REG 0x00 +#define MAJOR_VERSION_REG 0x01 +#define SCRATCHPAD_REG 0x02 + +#define FAN_TACH_REG_LOW(Id, Num) (0x10 * ((Num) + 1) + (Id) * 2) +#define FAN_TACH_REG_HIGH(Id, Num) (0x10 * ((Num) + 1) + ((Id) * 2) + 1) +#define FAN_TACH_A_REG_LOW(Id) (0x10 + ((Id) * 2)) +#define FAN_TACH_A_REG_HIGH(Id) (0x10 + ((Id) * 2) + 1) +#define FAN_TACH_B_REG_LOW(Id) (0x20 + ((Id) * 2)) +#define FAN_TACH_B_REG_HIGH(Id) (0x20 + ((Id) * 2) + 1) +#define FAN_PWM_REG(Id, Num) (0x30 + (Id) + ((Num) * 8)) +#define FAN_PWM_A_REG(Id) (0x30 + (Id)) +#define FAN_PWM_B_REG(Id) (0x38 + (Id)) + +#define FAN_ID_REG(Id) (0x41 + (Id)) +#define FAN_PRESENT_REG 0x49 +#define FAN_OK_REG 0x4A + +#define FAN_GREEN_LED_REG 0x4B +#define FAN_RED_LED_REG 0x4C + +#define FAN_INT_REG 0x4D +#define FAN_ID_CHNG_REG 0x4E +#define FAN_PRESENT_CHNG_REG 0x4F +#define FAN_OK_CHNG_REG 0x50 + +#define FAN_INT_OK (1 << 0) +#define FAN_INT_PRES (1 << 1) +#define FAN_INT_ID (1 << 2) + +#define FAN_LED_GREEN 1 +#define FAN_LED_RED 2 + +static bool managed_leds = true; +module_param(managed_leds, bool, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(managed_leds, "let the driver handle the leds"); + +static unsigned long poll_interval = 0; +module_param(poll_interval, ulong, S_IRUSR); +MODULE_PARM_DESC(poll_interval, "interval between two polling in ms"); + +static struct workqueue_struct *rook_cpld_workqueue; + +enum cpld_type { + ROOK_CPLD = 0, +}; + +struct cpld_info { + enum cpld_type id; + u8 fan_count; + u8 rotors; + int pulses; + int hz; +}; + +// these info could also be deducted from the id register +static struct cpld_info cpld_infos[] = { + [ROOK_CPLD] = { + .id = ROOK_CPLD, + .fan_count = 4, + .rotors = 1, + .pulses = 2, + .hz = 100000, + }, +}; + +struct cpld_fan_data { + struct led_classdev cdev; + bool ok; + bool present; + bool forward; + u16 tach; + u8 pwm; + u8 ident; + u8 index; + char led_name[LED_NAME_MAX_SZ]; +}; + +struct cpld_data { + const struct cpld_info *info; + struct mutex lock; + struct i2c_client *client; + struct device *hwmon_dev; + struct delayed_work dwork; + struct cpld_fan_data fans[MAX_FAN_COUNT]; + + const struct attribute_group *groups[1 + MAX_FAN_COUNT + 1]; + + u8 minor; + u8 major; + + u8 present; + u8 ok; + + u8 green_led; + u8 red_led; +}; + +static struct cpld_fan_data *fan_from_cpld(struct cpld_data *cpld, u8 fan_id) { + return &cpld->fans[fan_id]; +} + +static struct cpld_fan_data *fan_from_dev(struct device *dev, u8 fan_id) { + struct cpld_data *cpld = dev_get_drvdata(dev); + return fan_from_cpld(cpld, fan_id); +} + +static struct device *dev_from_cpld(struct cpld_data *cpld) { + return &cpld->client->dev; +} + +static s32 cpld_read_byte(struct cpld_data *cpld, u8 reg, u8 *res) +{ + int err; + + err = i2c_smbus_read_byte_data(cpld->client, reg); + if (err < 0) { + dev_err(&cpld->client->dev, + "failed to read reg 0x%02x error=%d\n", reg, err); + return err; + } + + *res = (err & 0xff); + return 0; +} + +static s32 cpld_write_byte(struct cpld_data *cpld, u8 reg, u8 byte) +{ + int err; + + err = i2c_smbus_write_byte_data(cpld->client, reg, byte); + if (err) { + dev_err(&cpld->client->dev, + "failed to write 0x%02x in reg 0x%02x error=%d\n", byte, reg, err); + } + + return err; +} + +static void cpld_work_start(struct cpld_data *cpld) +{ + if (poll_interval) { + queue_delayed_work(rook_cpld_workqueue, &cpld->dwork, + msecs_to_jiffies(poll_interval)); + } +} + +static s32 cpld_read_fan_id(struct cpld_data *cpld, u8 fan_id) +{ + struct cpld_fan_data *fan = fan_from_cpld(cpld, fan_id); + s32 err; + u8 tmp; + + err = cpld_read_byte(cpld, FAN_ID_REG(fan_id), &tmp); + if (err) + return err; + + fan->ident = tmp & 0xf; + fan->forward = (tmp >> 4) & 0x1; + + return 0; +} + +static int cpld_update_leds(struct cpld_data *cpld) +{ + struct cpld_fan_data *fan; + int err; + int i; + + cpld->green_led = 0; + cpld->red_led = 0; + + for (i = 0; i < cpld->info->fan_count; ++i) { + fan = fan_from_cpld(cpld, i); + if (fan->ok && fan->present) + cpld->green_led |= (1 << i); + else + cpld->red_led |= (1 << i); + } + + err = cpld_write_byte(cpld, FAN_GREEN_LED_REG, ~cpld->green_led); + if (err) + return err; + + err = cpld_write_byte(cpld, FAN_RED_LED_REG, ~cpld->red_led); + if (err) + return err; + + return 0; +} + +static int cpld_update(struct cpld_data *cpld) +{ + struct device *dev = dev_from_cpld(cpld); + struct cpld_fan_data *fan; + const char *str; + int fans_connected = 0; + int err; + int i; + u8 interrupt, id_chng, ok_chng, pres_chng; + + dev_dbg(dev, "polling cpld information\n"); + + err = cpld_read_byte(cpld, FAN_INT_REG, &interrupt); + if (err) + goto fail; + + if (interrupt & FAN_INT_ID) { + err = cpld_read_byte(cpld, FAN_ID_CHNG_REG, &id_chng); + if (err) + goto fail; + } + + if (interrupt & FAN_INT_OK) { + err = cpld_read_byte(cpld, FAN_OK_CHNG_REG, &ok_chng); + if (err) + goto fail; + err = cpld_read_byte(cpld, FAN_OK_REG, &cpld->ok); + if (err) + goto fail; + } + + if (interrupt & FAN_INT_PRES) { + err = cpld_read_byte(cpld, FAN_PRESENT_CHNG_REG, &pres_chng); + if (err) + goto fail; + err = cpld_read_byte(cpld, FAN_OK_REG, &cpld->present); + if (err) + goto fail; + } + + for (i = 0; i < cpld->info->fan_count; ++i) { + fan = fan_from_cpld(cpld, i); + + if ((interrupt & FAN_INT_PRES) && (pres_chng & (1 << i))) { + if (fan->present && (cpld->present & (1 << i))) { + str = "hotswapped"; + } else if (!fan->present && (cpld->present & (1 << i))) { + str = "plugged"; + fan->present = true; + } else { + str = "unplugged"; + fan->present = false; + } + dev_info(dev, "fan %d was %s\n", i + 1, str); + } + + if ((interrupt & FAN_INT_OK) && (ok_chng & (1 << i))) { + if (fan->ok && (cpld->ok & (1 << i))) { + dev_warn(dev, "fan %d had a small snag\n", i + 1); + } else if (fan->ok && !(cpld->ok & (1 << i))) { + dev_warn(dev, "fan %d is in fault, likely stuck\n", i + 1); + fan->ok = false; + } else { + dev_info(dev, "fan %d has recovered a running state\n", i + 1); + fan->ok = true; + } + } + + if ((interrupt & FAN_INT_ID) && (id_chng & (1 << i))) { + dev_info(dev, "fan %d kind has changed\n", i + 1); + cpld_read_fan_id(cpld, i); + } + + if (fan->present) + fans_connected += 1; + } + + if (cpld->info->fan_count - fans_connected > 1) { + dev_warn(dev, "it is not recommended to have more than one fan " + "unplugged. (%d/%d connected)\n", + fans_connected, cpld->info->fan_count); + } + + cpld_write_byte(cpld, FAN_ID_CHNG_REG, id_chng); + cpld_write_byte(cpld, FAN_OK_CHNG_REG, ok_chng); + cpld_write_byte(cpld, FAN_PRESENT_CHNG_REG, pres_chng); + + if (managed_leds) + err = cpld_update_leds(cpld); + +fail: + return err; +} + +static s32 cpld_write_pwm(struct cpld_data *cpld, u8 fan_id, u8 pwm) +{ + struct cpld_fan_data *fan = fan_from_cpld(cpld, fan_id); + int err = 0; + int i; + + for (i = 0; i < cpld->info->rotors; i++) { + err = cpld_write_byte(cpld, FAN_PWM_REG(fan_id, i), pwm); + if (err) + return err; + + fan->pwm = pwm; + } + + return err; +} + +static int cpld_read_present(struct cpld_data *cpld) +{ + struct cpld_fan_data *fan; + int err; + int i; + + err = cpld_read_byte(cpld, FAN_PRESENT_REG, &cpld->present); + if (err) + return err; + + for (i = 0; i < cpld->info->fan_count; ++i) { + fan = fan_from_cpld(cpld, i); + fan->present = !!(cpld->present & (1 << i)); + } + + return 0; +} + +static int cpld_read_fault(struct cpld_data *cpld) +{ + struct cpld_fan_data *fan; + int err; + int i; + + err = cpld_read_byte(cpld, FAN_OK_REG, &cpld->ok); + if (err) + return err; + + for (i = 0; i < cpld->info->fan_count; ++i) { + fan = fan_from_cpld(cpld, i); + fan->ok = !!(cpld->ok & (1 << i)); + } + + return 0; +} + +static s32 cpld_read_tach_single(struct cpld_data *cpld, u8 fan_id, u8 fan_num, + u16 *tach) +{ + int err; + u8 low; + u8 high; + + err = cpld_read_byte(cpld, FAN_TACH_REG_LOW(fan_id, fan_num), &low); + if (err) + return err; + + err = cpld_read_byte(cpld, FAN_TACH_REG_HIGH(fan_id, fan_num), &high); + if (err) + return err; + + *tach = ((u16)high << 8) | low; + + return 0; +} + +static s32 cpld_read_fan_tach(struct cpld_data *cpld, u8 fan_id) +{ + struct cpld_fan_data *fan = fan_from_cpld(cpld, fan_id); + s32 err = 0; + int i; + + for (i = 0; i < cpld->info->rotors; i++) { + err = cpld_read_tach_single(cpld, fan_id, i, &fan->tach); + if (err) + break; + + dev_dbg(dev_from_cpld(cpld), + "fan%d/%d tach=0x%04x\n", fan_id + 1, i + 1, fan->tach); + if (fan->tach == 0xffff) { + cpld_read_present(cpld); + cpld_read_fault(cpld); + if (managed_leds) + cpld_update_leds(cpld); + + if (!fan->present) + return -ENODEV; + + dev_warn(dev_from_cpld(cpld), + "Invalid tach information read from fan %d, this is likely " + "a hardware issue (stuck fan or broken register)\n", fan_id + 1); + + return -EIO; + } + } + + return err; +} + +static s32 cpld_read_pwm_single(struct cpld_data *cpld, u8 fan_id, u8 fan_num, + u8 *pwm) +{ + return cpld_read_byte(cpld, FAN_PWM_REG(fan_id, fan_num), pwm); +} + +static s32 cpld_read_fan_pwm(struct cpld_data *cpld, u8 fan_id) +{ + struct cpld_fan_data *fan = fan_from_cpld(cpld, fan_id); + int err; + u8 pwm_outer; + + err = cpld_read_pwm_single(cpld, fan_id, 0, &pwm_outer); + if (err) + return err; + + // some fans have two rotors but we only fetch the 1st one + fan->pwm = pwm_outer; + + return 0; +} + +static s32 cpld_read_fan_led(struct cpld_data *data, u8 fan_id, u8 *val) +{ + bool red = data->red_led & (1 << fan_id); + bool green = data->green_led & (1 << fan_id); + + *val = 0; + if (green) + *val += FAN_LED_GREEN; + if (red) + *val += FAN_LED_RED; + + return 0; +} + +static s32 cpld_write_fan_led(struct cpld_data *cpld, u8 fan_id, u8 val) +{ + int err = 0; + + if (val > 3) + return -EINVAL; + + if (val & FAN_LED_GREEN) + cpld->green_led |= (1 << fan_id); + else + cpld->green_led &= ~(1 << fan_id); + + if (val & FAN_LED_RED) + cpld->red_led |= (1 << fan_id); + else + cpld->red_led &= ~(1 << fan_id); + + err = cpld_write_byte(cpld, FAN_GREEN_LED_REG, ~cpld->green_led); + if (err) + return err; + + err = cpld_write_byte(cpld, FAN_RED_LED_REG, ~cpld->red_led); + + return err; +} + +static void brightness_set(struct led_classdev *led_cdev, + enum led_brightness val) +{ + struct cpld_fan_data *fan = container_of(led_cdev, struct cpld_fan_data, + cdev); + struct cpld_data *data = dev_get_drvdata(led_cdev->dev->parent); + + cpld_write_fan_led(data, fan->index, val); +} + +static enum led_brightness brightness_get(struct led_classdev *led_cdev) +{ + struct cpld_fan_data *fan = container_of(led_cdev, struct cpld_fan_data, + cdev); + struct cpld_data *data = dev_get_drvdata(led_cdev->dev->parent); + int err; + u8 val; + + err = cpld_read_fan_led(data, fan->index, &val); + if (err) + return 0; + + return val; +} + +static int led_init(struct cpld_fan_data *fan, struct i2c_client *client, + int fan_index) +{ + fan->index = fan_index; + fan->cdev.brightness_set = brightness_set; + fan->cdev.brightness_get = brightness_get; + scnprintf(fan->led_name, LED_NAME_MAX_SZ, "fan%d", fan->index + 1); + fan->cdev.name = fan->led_name; + + return led_classdev_register(&client->dev, &fan->cdev); +} + +static void cpld_leds_unregister(struct cpld_data *cpld, int num_leds) +{ + int i = 0; + struct cpld_fan_data *fan; + + for (i = 0; i < num_leds; i++) { + fan = fan_from_cpld(cpld, i); + led_classdev_unregister(&fan->cdev); + } +} + +static ssize_t cpld_fan_pwm_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpld_data *cpld = dev_get_drvdata(dev); + struct cpld_fan_data *fan = fan_from_cpld(cpld, attr->index); + int err; + + mutex_lock(&cpld->lock); + err = cpld_read_fan_pwm(cpld, attr->index); + mutex_unlock(&cpld->lock); + if (err) + return err; + + return sprintf(buf, "%hhu\n", fan->pwm); +} + +static ssize_t cpld_fan_pwm_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpld_data *cpld = dev_get_drvdata(dev); + u8 val; + int err; + + if (sscanf(buf, "%hhu", &val) != 1) + return -EINVAL; + + mutex_lock(&cpld->lock); + err = cpld_write_pwm(cpld, attr->index, val); + mutex_unlock(&cpld->lock); + if (err) + return err; + + return count; +} + +static ssize_t cpld_fan_present_show(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpld_data *cpld = dev_get_drvdata(dev); + struct cpld_fan_data *fan = fan_from_cpld(cpld, attr->index); + int err; + + if (!poll_interval) { + mutex_lock(&cpld->lock); + err = cpld_read_present(cpld); + mutex_unlock(&cpld->lock); + if (err) + return err; + } + + return sprintf(buf, "%d\n", fan->present); +} + +static ssize_t cpld_fan_id_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpld_data *cpld = dev_get_drvdata(dev); + struct cpld_fan_data *fan = fan_from_cpld(cpld, attr->index); + int err = 0; + + if (!poll_interval) { + mutex_lock(&cpld->lock); + err = cpld_read_fan_id(cpld, attr->index); + mutex_unlock(&cpld->lock); + if (err) + return err; + } + + return sprintf(buf, "%hhu\n", fan->ident); +} + +static ssize_t cpld_fan_fault_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpld_data *cpld = dev_get_drvdata(dev); + struct cpld_fan_data *fan = fan_from_cpld(cpld, attr->index); + int err; + + if (!poll_interval) { + mutex_lock(&cpld->lock); + err = cpld_read_fault(cpld); + mutex_unlock(&cpld->lock); + if (err) + return err; + } + + return sprintf(buf, "%d\n", !fan->ok); +} + +static ssize_t cpld_fan_tach_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpld_data *cpld = dev_get_drvdata(dev); + struct cpld_fan_data *fan = fan_from_cpld(cpld, attr->index); + int err; + int rpms; + + mutex_lock(&cpld->lock); + err = cpld_read_fan_tach(cpld, attr->index); + mutex_unlock(&cpld->lock); + if (err) + return err; + + if (!fan->tach) { + return -EINVAL; + } + + rpms = ((cpld->info->hz * 60) / fan->tach) / cpld->info->pulses; + + return sprintf(buf, "%d\n", rpms); +} + +static ssize_t cpld_fan_led_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpld_data *cpld = dev_get_drvdata(dev); + int err; + u8 val; + + err = cpld_read_fan_led(cpld, attr->index, &val); + if (err) + return err; + + return sprintf(buf, "%hhu\n", val); +} + +static ssize_t cpld_fan_led_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpld_data *cpld = dev_get_drvdata(dev); + int err; + u8 val; + + if (managed_leds) + return -EPERM; + + if (sscanf(buf, "%hhu", &val) != 1) + return -EINVAL; + + mutex_lock(&cpld->lock); + err = cpld_write_fan_led(cpld, attr->index, val); + mutex_unlock(&cpld->lock); + if (err) + return err; + + return count; +} + +static ssize_t cpld_fan_airflow_show(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct cpld_fan_data *fan = fan_from_dev(dev, attr->index); + return sprintf(buf, "%s\n", (fan->forward) ? "forward" : "reverse"); +} + + +#define FAN_DEVICE_ATTR(_name) \ + static SENSOR_DEVICE_ATTR(pwm## _name, S_IRUGO|S_IWGRP|S_IWUSR, \ + cpld_fan_pwm_show, cpld_fan_pwm_store, _name-1); \ + static SENSOR_DEVICE_ATTR(fan##_name##_id, S_IRUGO, \ + cpld_fan_id_show, NULL, _name-1); \ + static SENSOR_DEVICE_ATTR(fan##_name##_input, S_IRUGO, \ + cpld_fan_tach_show, NULL, _name-1); \ + static SENSOR_DEVICE_ATTR(fan##_name##_fault, S_IRUGO, \ + cpld_fan_fault_show, NULL, _name-1); \ + static SENSOR_DEVICE_ATTR(fan##_name##_present, S_IRUGO, \ + cpld_fan_present_show, NULL, _name-1); \ + static SENSOR_DEVICE_ATTR(fan##_name##_led, S_IRUGO|S_IWGRP|S_IWUSR, \ + cpld_fan_led_show, cpld_fan_led_store, _name-1); \ + static SENSOR_DEVICE_ATTR(fan##_name##_airflow, S_IRUGO, \ + cpld_fan_airflow_show, NULL, _name-1); + +#define FAN_ATTR(_name) \ + &sensor_dev_attr_pwm##_name.dev_attr.attr, \ + &sensor_dev_attr_fan##_name##_id.dev_attr.attr, \ + &sensor_dev_attr_fan##_name##_input.dev_attr.attr, \ + &sensor_dev_attr_fan##_name##_fault.dev_attr.attr, \ + &sensor_dev_attr_fan##_name##_present.dev_attr.attr, \ + &sensor_dev_attr_fan##_name##_led.dev_attr.attr, \ + &sensor_dev_attr_fan##_name##_airflow.dev_attr.attr \ + + +#define FAN_ATTR_GROUP(_name) &fan##_name##_attr_group + +#define DEVICE_FAN_ATTR_GROUP(_name) \ + FAN_DEVICE_ATTR(_name); \ + static struct attribute *fan##_name##_attrs[] = { FAN_ATTR(_name), NULL }; \ + static struct attribute_group fan##_name##_attr_group = { \ + .attrs = fan##_name##_attrs, \ + } + +DEVICE_FAN_ATTR_GROUP(1); +DEVICE_FAN_ATTR_GROUP(2); +DEVICE_FAN_ATTR_GROUP(3); +DEVICE_FAN_ATTR_GROUP(4); +DEVICE_FAN_ATTR_GROUP(5); +DEVICE_FAN_ATTR_GROUP(6); +DEVICE_FAN_ATTR_GROUP(7); +DEVICE_FAN_ATTR_GROUP(8); + +static struct attribute_group *fan_groups[] = { + FAN_ATTR_GROUP(1), + FAN_ATTR_GROUP(2), + FAN_ATTR_GROUP(3), + FAN_ATTR_GROUP(4), + FAN_ATTR_GROUP(5), + FAN_ATTR_GROUP(6), + FAN_ATTR_GROUP(7), + FAN_ATTR_GROUP(8), + NULL, +}; + +static ssize_t cpld_revision_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cpld_data *cpld = dev_get_drvdata(dev); + return sprintf(buf, "%02x.%02x\n", cpld->major, cpld->minor); +} + +DEVICE_ATTR(cpld_revision, S_IRUGO, cpld_revision_show, NULL); + +static ssize_t cpld_update_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cpld_data *cpld = dev_get_drvdata(dev); + int err; + + mutex_lock(&cpld->lock); + err = cpld_update(cpld); + mutex_unlock(&cpld->lock); + + return err; +} + +DEVICE_ATTR(update, S_IRUGO, cpld_update_show, NULL); + +static struct attribute *cpld_attrs[] = { + &dev_attr_cpld_revision.attr, + &dev_attr_update.attr, + NULL, +}; + +static struct attribute_group cpld_group = { + .attrs = cpld_attrs, +}; + +static void cpld_work_fn(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct cpld_data *cpld = container_of(dwork, struct cpld_data, dwork); + + mutex_lock(&cpld->lock); + cpld_update(cpld); + cpld_work_start(cpld); + mutex_unlock(&cpld->lock); +} + +static int cpld_init(struct cpld_data *cpld) +{ + struct cpld_fan_data *fan; + int err; + int i; + + err = cpld_read_byte(cpld, MINOR_VERSION_REG, &cpld->minor); + if (err) + return -ENODEV; + + err = cpld_read_byte(cpld, MAJOR_VERSION_REG, &cpld->major); + if (err) + return err; + + dev_info(dev_from_cpld(cpld), "rook CPLD version %02x.%02x\n", + cpld->major, cpld->minor); + + err = cpld_read_byte(cpld, FAN_PRESENT_REG, &cpld->present); + if (err) + return err; + + err = cpld_read_byte(cpld, FAN_OK_REG, &cpld->ok); + if (err) + return err; + + for (i = 0; i < cpld->info->fan_count; ++i) { + fan = fan_from_cpld(cpld, i); + fan->present = !!(cpld->present & (1 << i)); + fan->ok = !!(cpld->ok & (1 << i)); + if (fan->present) { + cpld_read_fan_id(cpld, i); + cpld_read_fan_tach(cpld, i); + cpld_read_fan_pwm(cpld, i); + err = led_init(fan, cpld->client, i); + if (err) { + cpld_leds_unregister(cpld, i); + return err; + } + } + } + + cpld_write_byte(cpld, FAN_OK_CHNG_REG, 0xff); + cpld_write_byte(cpld, FAN_ID_CHNG_REG, 0xff); + cpld_write_byte(cpld, FAN_ID_CHNG_REG, 0xff); + + if (managed_leds) { + err = cpld_update_leds(cpld); + if (err) + return err; + } + + INIT_DELAYED_WORK(&cpld->dwork, cpld_work_fn); + cpld_work_start(cpld); + + return err; +} + +static int cpld_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct cpld_data *cpld; + int err; + int i; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(dev, "adapter doesn't support byte transactions\n"); + return -ENODEV; + } + + cpld = devm_kzalloc(dev, sizeof(struct cpld_data), GFP_KERNEL); + if (!cpld) + return -ENOMEM; + + i2c_set_clientdata(client, cpld); + cpld->client = client; + + cpld->info = &cpld_infos[id->driver_data]; + mutex_init(&cpld->lock); + + cpld->groups[0] = &cpld_group; + for (i = 0; i < cpld->info->fan_count; ++i) { + cpld->groups[i + 1] = fan_groups[i]; + } + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + cpld, cpld->groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + cpld->hwmon_dev = hwmon_dev; + + mutex_lock(&cpld->lock); + err = cpld_init(cpld); + mutex_unlock(&cpld->lock); + + return err; +} + +static int cpld_remove(struct i2c_client *client) +{ + struct cpld_data *cpld = i2c_get_clientdata(client); + + mutex_lock(&cpld->lock); + cancel_delayed_work_sync(&cpld->dwork); + mutex_unlock(&cpld->lock); + + cpld_leds_unregister(cpld, cpld->info->fan_count); + + return 0; +} + +static const struct i2c_device_id cpld_id[] = { + { "rook_cpld", ROOK_CPLD }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cpld_id); + +static struct i2c_driver cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRIVER_NAME, + }, + .id_table = cpld_id, + .probe = cpld_probe, + .remove = cpld_remove, +}; + +static int __init rook_cpld_init(void) +{ + int err; + + rook_cpld_workqueue = create_singlethread_workqueue(DRIVER_NAME); + if (IS_ERR_OR_NULL(rook_cpld_workqueue)) { + pr_err("failed to initialize workqueue\n"); + return PTR_ERR(rook_cpld_workqueue); + } + + err = i2c_add_driver(&cpld_driver); + if (err < 0) { + destroy_workqueue(rook_cpld_workqueue); + rook_cpld_workqueue = NULL; + return err; + } + + return 0; +} + +static void __exit rook_cpld_exit(void) +{ + i2c_del_driver(&cpld_driver); + destroy_workqueue(rook_cpld_workqueue); + rook_cpld_workqueue = NULL; +} + +module_init(rook_cpld_init); +module_exit(rook_cpld_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arista Networks"); +MODULE_DESCRIPTION("Rook fan cpld"); diff --git a/platform/broadcom/sonic-platform-modules-arista/src/rook-led-driver.c b/platform/broadcom/sonic-platform-modules-arista/src/rook-led-driver.c new file mode 100644 index 000000000000..090da18f9050 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/src/rook-led-driver.c @@ -0,0 +1,296 @@ +/* Copyright (c) 2017 Arista Networks, Inc. + * + * 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, see . + * + */ + +#include +#include +#include +#include +#include + +// led -> gpio pin mapping +#define PS2_STATUS_LEDR 0 +#define PS2_STATUS_LEDG 1 +#define PS1_STATUS_LEDR 2 +#define PS1_STATUS_LEDG 3 +#define FAN_STATUS_LEDR 4 +#define FAN_STATUS_LEDG 5 +#define STATUS_LEDR 6 +#define STATUS_LEDG 7 +#define STATUS_LEDB 8 + +// pca9555 register mapping +#define INPUT_PORT_0 0 +#define INPUT_PORT_1 1 +#define OUTPUT_PORT_0 2 +#define OUTPUT_PORT_1 3 +#define POL_INV_PORT_0 4 +#define POL_INV_PORT_1 5 +#define CONFIG_PORT_0 6 +#define CONFIG_PORT_1 7 + +// pca9555 bank info +#define BANK_SZ 8 +#define NGPIO 16 +#define NUM_BANKS 2 +#define BANK_SHIFT 1 +#define NUM_LEDS 9 + +#define LED_ON 1 +#define OUTPUT 0 +#define INPUT 1 + +#define LED_NAME_MAX_SZ 40 + +struct pca9555_led { + char name[LED_NAME_MAX_SZ]; + int pin; + int on; // value to write to pin for led to turn on + struct led_classdev cdev; +}; + +struct pca9555_chip { + u8 reg_output[NUM_BANKS]; + u8 reg_direction[NUM_BANKS]; + int num_leds; + char *dev_name; + struct mutex i2c_lock; + struct i2c_client *client; + struct pca9555_led leds[NUM_LEDS]; +}; + +static u8 new_reg_val(u8 reg_val_prev, int pin, int val) +{ + if (val) + return reg_val_prev | (1u << (pin % BANK_SZ)); + else + return reg_val_prev & ~(1u << (pin % BANK_SZ)); +} + +static int pca9555_write_single(struct pca9555_chip *chip, int reg, int val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(chip->client, reg, val); + if (ret < 0) { + dev_err(&chip->client->dev, "Failed writing register %d\n", reg); + return ret; + } + + return 0; +} + +static int pca9555_set_value(struct pca9555_chip *chip, int pin, int val) +{ + int reg, ret; + u8 reg_val; + + mutex_lock(&chip->i2c_lock); + + reg_val = new_reg_val(chip->reg_output[pin / BANK_SZ], pin, val); + if (pin >= BANK_SZ) + reg = OUTPUT_PORT_1; + else + reg = OUTPUT_PORT_0; + ret = pca9555_write_single(chip, reg, reg_val); + + if (!ret) + chip->reg_output[pin / BANK_SZ] = reg_val; + + mutex_unlock(&chip->i2c_lock); + + return ret; +} + +static void brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct pca9555_led *pled = container_of(led_cdev, struct pca9555_led, + cdev); + struct pca9555_chip *chip = dev_get_drvdata(led_cdev->dev->parent); + + if ((int)value == LED_ON) + pca9555_set_value(chip, pled->pin, pled->on); + else + pca9555_set_value(chip, pled->pin, !pled->on); +} + +static void set_led_name(struct i2c_client *client, char *name, const char *color, + const char *fun) +{ + struct pca9555_chip *chip = dev_get_drvdata(&client->dev); + + scnprintf(name, LED_NAME_MAX_SZ, "%s-%d:%s:%s", chip->dev_name, + client->adapter->nr, color, fun); +} + +static void led_init(struct pca9555_led *led, struct i2c_client *client, + const char *color, const char *fun, int pin, int on) +{ + set_led_name(client, led->name, color, fun); + led->cdev.name = led->name; + led->pin = pin; + led->on = on; + led->cdev.brightness_set = brightness_set; +} + +static void leds_init(struct pca9555_led *leds, struct i2c_client *client) +{ + led_init(&leds[0], client, "green", "status", STATUS_LEDG, 0); + led_init(&leds[1], client, "red", "status", STATUS_LEDR, 0); + led_init(&leds[2], client, "green", "fan_status", FAN_STATUS_LEDG, 0); + led_init(&leds[3], client, "red", "fan_status", FAN_STATUS_LEDR, 0); + led_init(&leds[4], client, "green", "psu1_status", PS1_STATUS_LEDG, 0); + led_init(&leds[5], client, "red", "psu1_status", PS1_STATUS_LEDR, 0); + led_init(&leds[6], client, "green", "psu2_status", PS2_STATUS_LEDG, 0); + led_init(&leds[7], client, "red", "psu2_status", PS2_STATUS_LEDR, 0); + led_init(&leds[8], client, "blue", "beacon", STATUS_LEDB, 1); +} + +static int leds_pca9555_detect(struct i2c_client *client) +{ + if (i2c_smbus_read_byte_data(client, CONFIG_PORT_0) < 0) + return -ENODEV; + return 0; +} + +static int leds_pca9555_all_output(struct i2c_client *client) +{ + int err; + struct pca9555_chip *chip; + + chip = dev_get_drvdata(&client->dev); + + err = i2c_smbus_write_byte_data(client, CONFIG_PORT_0, OUTPUT); + if (err) + return err; + chip->reg_direction[0] = OUTPUT; + + err = i2c_smbus_write_byte_data(client, CONFIG_PORT_1, OUTPUT); + if (err) + return err; + chip->reg_direction[1] = OUTPUT; + + return 0; +} + +static int leds_pca9555_all_set(struct i2c_client *client, u8 bank1_val, + u8 bank2_val) +{ + int err; + struct pca9555_chip *chip; + + chip = dev_get_drvdata(&client->dev); + + err = i2c_smbus_write_byte_data(client, OUTPUT_PORT_0, bank1_val); + if (err) + return err; + chip->reg_output[0] = bank1_val; + + err = i2c_smbus_write_byte_data(client, OUTPUT_PORT_1, bank2_val); + if (err) + return err; + chip->reg_output[1] = bank2_val; + + return 0; +} + +static int leds_pca9555_remove(struct i2c_client *client) +{ + int i; + struct pca9555_chip *chip = i2c_get_clientdata(client); + + for (i = 0; i < chip->num_leds; i++) + led_classdev_unregister(&chip->leds[i].cdev); + + return 0; +} + +static int leds_pca9555_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int i, err; + struct pca9555_chip *chip; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "adapter doesn't support byte transactions\n"); + return -ENODEV; + } + + err = leds_pca9555_detect(client); + if (err) + return err; + + chip = devm_kzalloc(&client->dev, sizeof(struct pca9555_chip), + GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + mutex_init(&chip->i2c_lock); + chip->client = client; + chip->dev_name = client->name; + i2c_set_clientdata(client, chip); + + leds_init(chip->leds, client); + + // configure all pins as output and off from the start + err = leds_pca9555_all_output(client); + if (err) + return err; + + err = leds_pca9555_all_set(client, 0xff, 0); + if (err) + return err; + + // then set all leds to green + err = leds_pca9555_all_set(client, 0x55, 0); + if (err) + return err; + + chip->num_leds = 0; + for (i = 0; i < NUM_LEDS; i++) { + err = led_classdev_register(&client->dev, &chip->leds[i].cdev); + if (err) { + leds_pca9555_remove(client); + return err; + } + chip->num_leds += 1; + } + + return 0; +} + +static const struct i2c_device_id leds_pca9555_id[] = { + {"rook_leds", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, leds_pca9555_id); + +static struct i2c_driver leds_pca9555_driver = { + .driver = { + .name = "rook_leds" + }, + .probe = leds_pca9555_probe, + .remove = leds_pca9555_remove, + .id_table = leds_pca9555_id, +}; + +module_i2c_driver(leds_pca9555_driver); + +MODULE_AUTHOR("Arista Networks"); +MODULE_DESCRIPTION("Driver to manage Rook status leds"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-arista/src/scd-hwmon.c b/platform/broadcom/sonic-platform-modules-arista/src/scd-hwmon.c new file mode 100644 index 000000000000..4509bf7629f3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/src/scd-hwmon.c @@ -0,0 +1,1595 @@ +/* Copyright (c) 2017 Arista Networks, Inc. + * + * 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, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scd.h" +#include "scd-hwmon.h" + +#define SCD_MODULE_NAME "scd-hwmon" + +#define SMBUS_REQUEST_OFFSET 0x10 +#define SMBUS_CONTROL_STATUS_OFFSET 0x20 +#define SMBUS_RESPONSE_OFFSET 0x30 + +#define RESET_SET_OFFSET 0x00 +#define RESET_CLEAR_OFFSET 0x10 + +#define MASTER_DEFAULT_BUS_COUNT 8 +#define MASTER_DEFAULT_MAX_RETRIES 3 + +#define MAX_CONFIG_LINE_SIZE 100 + +struct scd_context; + +struct scd_master { + struct scd_context *ctx; + struct list_head list; + + u32 id; + u32 req; + u32 cs; + u32 resp; + struct mutex mutex; + struct list_head bus_list; + + int max_retries; +}; + +struct bus_params { + struct list_head list; + u16 addr; + u8 t; + u8 datw; + u8 datr; +}; + +const struct bus_params default_bus_params = { + .t = 1, + .datw = 3, + .datr = 3, +}; + +struct scd_bus { + struct scd_master *master; + struct list_head list; + + u32 id; + struct list_head params; + + struct i2c_adapter adap; +}; + +#define LED_NAME_MAX_SZ 40 +struct scd_led { + struct scd_context *ctx; + struct list_head list; + + u32 addr; + char name[LED_NAME_MAX_SZ]; + struct led_classdev cdev; +}; + +struct scd_gpio_attribute { + struct device_attribute dev_attr; + struct scd_context *ctx; + + u32 addr; + u32 bit; + u32 active_low; +}; + +#define GPIO_NAME_MAX_SZ 50 +struct scd_gpio { + char name[GPIO_NAME_MAX_SZ]; + struct scd_gpio_attribute attr; + struct list_head list; +}; + +#define __ATTR_NAME_PTR(_name, _mode, _show, _store) { \ + .attr = { .name = _name, \ + .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \ + .show = _show, \ + .store = _store, \ +} + +#define to_scd_gpio_attr(_dev_attr) \ + container_of(_dev_attr, struct scd_gpio_attribute, dev_attr) + +#define SCD_GPIO_ATTR(_name, _mode, _show, _store, _ctx, _addr, _bit, _active_low) \ + { .dev_attr = __ATTR_NAME_PTR(_name, _mode, _show, _store), \ + .ctx = _ctx, \ + .addr = _addr, \ + .bit = _bit, \ + .active_low = _active_low \ + } + +#define SCD_RW_GPIO_ATTR(_name, _ctx, _addr, _bit, _active_low) \ + SCD_GPIO_ATTR(_name, S_IRUGO | S_IWUSR, attribute_gpio_get, attribute_gpio_set, \ + _ctx, _addr, _bit, _active_low) + +#define SCD_RO_GPIO_ATTR(_name, _ctx, _addr, _bit, _active_low) \ + SCD_GPIO_ATTR(_name, S_IRUGO, attribute_gpio_get, NULL, \ + _ctx, _addr, _bit, _active_low) + +struct scd_reset_attribute { + struct device_attribute dev_attr; + struct scd_context *ctx; + + u32 addr; + u32 bit; +}; + +#define RESET_NAME_MAX_SZ 50 +struct scd_reset { + char name[RESET_NAME_MAX_SZ]; + struct scd_reset_attribute attr; + struct list_head list; +}; + +#define to_scd_reset_attr(_dev_attr) \ + container_of(_dev_attr, struct scd_reset_attribute, dev_attr) + +#define SCD_RESET_ATTR(_name, _ctx, _addr, _bit) \ + { .dev_attr = __ATTR_NAME_PTR(_name, S_IRUGO | S_IWUSR, attribute_reset_get, \ + attribute_reset_set), \ + .ctx = _ctx, \ + .addr = _addr, \ + .bit = _bit, \ + } + +struct scd_context { + struct pci_dev *pdev; + size_t res_size; + + struct list_head list; + + struct mutex mutex; + bool initialized; + + struct list_head gpio_list; + struct list_head reset_list; + struct list_head led_list; + struct list_head master_list; +}; + +union request_reg { + u32 reg; + struct { + u32 d:8; + u32 ss:6; + u32 reserved1:2; + u32 dat:2; + u32 t:2; + u32 sp:1; + u32 da:1; + u32 dod:1; + u32 st:1; + u32 bs:4; + u32 ti:4; + } __packed; +}; + +union ctrl_status_reg { + u32 reg; + struct { + u32 reserved1:13; + u32 foe:1; + u32 reserved2:17; + u32 reset:1; + } __packed; +}; + +union response_reg { + u32 reg; + struct { + u32 d:8; + u32 bus_conflict_error:1; + u32 timeout_error:1; + u32 ack_error:1; + u32 flushed:1; + u32 ti:4; + u32 ss:6; + u32 reserved2:9; + u32 fe:1; + } __packed; +}; + +/* locking functions */ +static struct mutex scd_hwmon_mutex; + +static void module_lock(void) +{ + mutex_lock(&scd_hwmon_mutex); +} + +static void module_unlock(void) +{ + mutex_unlock(&scd_hwmon_mutex); +} + +static void master_lock(struct scd_master *master) +{ + mutex_lock(&master->mutex); +} + +static void master_unlock(struct scd_master *master) +{ + mutex_unlock(&master->mutex); +} + +static void scd_lock(struct scd_context *ctx) +{ + mutex_lock(&ctx->mutex); +} + +static void scd_unlock(struct scd_context *ctx) +{ + mutex_unlock(&ctx->mutex); +} + +/* SMBus functions */ +static void smbus_master_write_req(struct scd_master *master, + union request_reg req) +{ + u32 addr = (u32)master->req; + scd_write_register(master->ctx->pdev, addr, req.reg); +} + +static void smbus_master_write_cs(struct scd_master *master, + union ctrl_status_reg cs) +{ + scd_write_register(master->ctx->pdev, master->cs, cs.reg); +} + +static union ctrl_status_reg smbus_master_read_cs(struct scd_master *master) +{ + union ctrl_status_reg cs; + cs.reg = scd_read_register(master->ctx->pdev, master->cs); + return cs; +} + +static union response_reg smbus_master_read_resp(struct scd_master *master) +{ + union response_reg resp; + u32 retries = 10; + + resp.reg = scd_read_register(master->ctx->pdev, master->resp); + + while (resp.fe && --retries) { + msleep(10); + resp.reg = scd_read_register(master->ctx->pdev, master->resp); + } + + if (resp.fe) { + scd_dbg("smbus response: fifo still empty after retries"); + resp.reg = 0xffffffff; + } + + return resp; +} + +static s32 smbus_check_resp(union response_reg resp, u32 tid) +{ + const char *error; + int error_ret = -EIO; + + if (resp.reg == 0xffffffff) { + error = "fe"; + goto fail; + } + if (resp.ack_error) { + error = "ack"; + goto fail; + } + if (resp.timeout_error) { + error = "timeout"; + goto fail; + } + if (resp.bus_conflict_error) { + error = "conflict"; + goto fail; + } + if (resp.flushed) { + error = "flush"; + goto fail; + } + if (resp.ti != tid) { + error = "tid"; + goto fail; + } + + return 0; + +fail: + scd_dbg("smbus response: %s error. reg=0x%08x", error, resp.reg); + return error_ret; +} + +static u32 scd_smbus_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static void smbus_master_reset(struct scd_master *master) +{ + union ctrl_status_reg cs; + cs = smbus_master_read_cs(master); + cs.reset = 1; + cs.foe = 1; + smbus_master_write_cs(master, cs); + mdelay(10); + cs.reset = 0; + smbus_master_write_cs(master, cs); +} + +static const struct bus_params *get_bus_params(struct scd_bus *bus, u16 addr) { + const struct bus_params *params = &default_bus_params; + struct bus_params *params_tmp; + + list_for_each_entry(params_tmp, &bus->params, list) { + if (params_tmp->addr == addr) { + params = params_tmp; + break; + } + } + + return params; +} + +static s32 scd_smbus_do(struct scd_bus *bus, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + struct scd_master *master = bus->master; + const struct bus_params *params; + int i; + union request_reg req; + union response_reg resp; + int ret = 0; + u32 ss = 0; + u32 data_offset = 0; + + master_lock(master); + + params = get_bus_params(bus, addr); + + req.reg = 0; + req.bs = bus->id; + req.t = params->t; + + switch (size) { + case I2C_SMBUS_QUICK: + ss = 1; + break; + case I2C_SMBUS_BYTE: + ss = 2; + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_WRITE) { + ss = 3; + } else { + ss = 4; + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_WRITE) { + ss = 4; + } else { + ss = 5; + } + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + data_offset = 1; + if (read_write == I2C_SMBUS_WRITE) { + ss = 2 + data->block[0]; + } else { + ss = 3 + data->block[0]; + } + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_WRITE) { + ss = 3 + data->block[0]; + } else { + master_unlock(master); + ret = scd_smbus_do(bus, addr, flags, I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, data); + master_lock(master); + if (ret) { + goto fail; + } + ss = 4 + data->block[0]; + } + break; + } + + req.st = 1; + req.ss = ss; + req.d = (((addr & 0xff) << 1) | ((ss <= 2) ? read_write : 0)); + req.dod = 1; + for (i = 0; i < ss; i++) { + if (i == ss - 1) { + req.sp = 1; + if (read_write == I2C_SMBUS_WRITE) { + req.dat = params->datw; + } else { + req.dat = params->datr; + } + } + if (i == 1) { + req.st = 0; + req.ss = 0; + req.d = command; + if (ss == 2) + req.dod = ((read_write == I2C_SMBUS_WRITE) ? 1 : 0); + else + req.dod = 1; + } + if ((i == 2 && read_write == I2C_SMBUS_READ)) { + req.st = 1; + req.d = (((addr & 0xff) << 1) | 1); + } + if (i >= 2 && (read_write == I2C_SMBUS_WRITE)) { + req.d = data->block[data_offset + i - 2]; + } + if ((i == 3 && read_write == I2C_SMBUS_READ)) { + req.dod = 0; + } + req.da = ((!(req.dod || req.sp)) ? 1 : 0); + smbus_master_write_req(master, req); + req.ti++; + req.st = 0; + } + + req.ti = 0; + for (i = 0; i < ss; i++) { + resp = smbus_master_read_resp(master); + ret = smbus_check_resp(resp, req.ti); + if (ret) { + goto fail; + } + req.ti++; + if (read_write == I2C_SMBUS_READ) { + if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA) { + if (i == ss - 1) { + data->byte = resp.d; + } + } else if (size == I2C_SMBUS_WORD_DATA) { + if (i == ss - 2) { + data->word = resp.d; + } else if (i == ss - 1) { + data->word |= (resp.d << 8); + } + } else { + if (i >= 3) { + if (size == I2C_SMBUS_BLOCK_DATA) { + data->block[i - 3] = resp.d; + } else { + data->block[i - 2] = resp.d; + } + } + } + } + } + + master_unlock(master); + return 0; + +fail: + scd_dbg("smbus %s failed addr=0x%02x reg=0x%02x size=0x%02x adapter=\"%s\"\n", + (read_write) ? "read" : "write", addr, command, size, bus->adap.name); + smbus_master_reset(master); + master_unlock(master); + return ret; +} + +static s32 scd_smbus_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + struct scd_bus *bus = i2c_get_adapdata(adap); + struct scd_master *master = bus->master; + int retry = 0; + int ret; + + scd_dbg("smbus %s do addr=0x%02x reg=0x%02x size=0x%02x adapter=\"%s\"\n", + (read_write) ? "read" : "write", addr, command, size, bus->adap.name); + do { + ret = scd_smbus_do(bus, addr, flags, read_write, command, size, data); + if (ret != -EIO) + return ret; + retry++; + scd_dbg("smbus retrying... %d/%d", retry, master->max_retries); + } while (retry < master->max_retries); + + scd_warn("smbus %s failed addr=0x%02x reg=0x%02x size=0x%02x " + "adapter=\"%s\"\n", (read_write) ? "read" : "write", + addr, command, size, bus->adap.name); + + return -EIO; +} + +static struct i2c_algorithm scd_smbus_algorithm = { + .smbus_xfer = scd_smbus_access, + .functionality = scd_smbus_func, +}; + +static struct list_head scd_list; + +static struct scd_context *get_context_for_pdev(struct pci_dev *pdev) +{ + struct scd_context *ctx; + + module_lock(); + list_for_each_entry(ctx, &scd_list, list) { + if (ctx->pdev == pdev) { + module_unlock(); + return ctx; + } + } + module_unlock(); + + return NULL; +} + +static struct scd_context *get_context_for_dev(struct device *dev) +{ + struct scd_context *ctx; + + module_lock(); + list_for_each_entry(ctx, &scd_list, list) { + if (&ctx->pdev->dev == dev) { + module_unlock(); + return ctx; + } + } + module_unlock(); + + return NULL; +} + +static int scd_smbus_bus_add(struct scd_master *master, int id) +{ + struct scd_bus *bus; + int err; + + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (!bus) { + return -ENOMEM; + } + + bus->master = master; + bus->id = id; + INIT_LIST_HEAD(&bus->params); + bus->adap.owner = THIS_MODULE; + bus->adap.class = 0; + bus->adap.algo = &scd_smbus_algorithm; + bus->adap.dev.parent = &master->ctx->pdev->dev; + scnprintf(bus->adap.name, + sizeof(bus->adap.name), + "SCD %s SMBus master %d bus %d", pci_name(master->ctx->pdev), + master->id, bus->id); + i2c_set_adapdata(&bus->adap, bus); + err = i2c_add_adapter(&bus->adap); + if (err) { + kfree(bus); + return err; + } + + master_lock(master); + list_add_tail(&bus->list, &master->bus_list); + master_unlock(master); + + return 0; +} + +static void scd_smbus_master_remove(struct scd_master *master) +{ + struct scd_bus *bus; + struct scd_bus *tmp_bus; + struct bus_params *params; + struct bus_params *tmp_params; + + list_for_each_entry_safe(bus, tmp_bus, &master->bus_list, list) { + i2c_del_adapter(&bus->adap); + + list_for_each_entry_safe(params, tmp_params, &bus->params, list) { + list_del(¶ms->list); + kfree(params); + } + + list_del(&bus->list); + kfree(bus); + } + + smbus_master_reset(master); + + list_del(&master->list); + kfree(master); +} + +static void scd_smbus_remove_all(struct scd_context *ctx) +{ + struct scd_master *master; + struct scd_master *tmp_master; + + list_for_each_entry_safe(master, tmp_master, &ctx->master_list, list) { + scd_smbus_master_remove(master); + } +} + +static int scd_smbus_master_add(struct scd_context *ctx, u32 addr, u32 id, + u32 bus_count) +{ + struct scd_master *master; + int err = 0; + int i; + + list_for_each_entry(master, &ctx->master_list, list) { + if (master->id == id) { + return -EEXIST; + } + } + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) { + return -ENOMEM; + } + + master->ctx = ctx; + mutex_init(&master->mutex); + master->id = id; + master->req = addr + SMBUS_REQUEST_OFFSET; + master->cs = addr + SMBUS_CONTROL_STATUS_OFFSET; + master->resp = addr + SMBUS_RESPONSE_OFFSET; + master->max_retries = MASTER_DEFAULT_MAX_RETRIES; + INIT_LIST_HEAD(&master->bus_list); + + for (i = 0; i < bus_count; ++i) { + err = scd_smbus_bus_add(master, i); + if (err) { + goto fail_bus; + } + } + + smbus_master_reset(master); + + list_add_tail(&master->list, &ctx->master_list); + + return 0; + +fail_bus: + scd_smbus_master_remove(master); + return err; +} + +static void led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct scd_led *led = container_of(led_cdev, struct scd_led, cdev); + u32 reg; + + switch ((int)value) { + case 0: + reg = 0x0006ff00; + break; + case 1: + reg = 0x1006ff00; + break; + case 2: + reg = 0x0806ff00; + break; + case 3: + reg = 0x1806ff00; + break; + case 4: + reg = 0x1406ff00; + break; + case 5: + reg = 0x0C06ff00; + break; + case 6: + reg = 0x1C06ff00; + break; + default: + reg = 0x1806ff00; + break; + } + scd_write_register(led->ctx->pdev, led->addr, reg); +} + +static void scd_led_remove_all(struct scd_context *ctx) +{ + struct scd_led *led; + struct scd_led *led_tmp; + + list_for_each_entry_safe(led, led_tmp, &ctx->led_list, list) { + led_classdev_unregister(&led->cdev); + list_del(&led->list); + kfree(led); + } +} + +static struct scd_led *scd_led_find(struct scd_context *ctx, u32 addr) +{ + struct scd_led *led; + + list_for_each_entry(led, &ctx->led_list, list) { + if (led->addr == addr) + return led; + } + return NULL; +} + +static int scd_led_add(struct scd_context *ctx, const char *name, u32 addr) +{ + struct scd_led *led; + int ret; + + if (scd_led_find(ctx, addr)) + return -EEXIST; + + led = kzalloc(sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->ctx = ctx; + led->addr = addr; + strncpy(led->name, name, FIELD_SIZEOF(typeof(*led), name)); + INIT_LIST_HEAD(&led->list); + + led->cdev.name = led->name; + led->cdev.brightness_set = led_brightness_set; + + ret = led_classdev_register(&ctx->pdev->dev, &led->cdev); + if (ret) { + kfree(led); + return ret; + } + + list_add_tail(&led->list, &ctx->led_list); + + return 0; +} + +static ssize_t attribute_gpio_get(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + const struct scd_gpio_attribute *gpio = to_scd_gpio_attr(devattr); + u32 reg = scd_read_register(gpio->ctx->pdev, gpio->addr); + u32 res = !!(reg & (1 << gpio->bit)); + res = (gpio->active_low) ? !res : res; + return sprintf(buf, "%u\n", res); +} + +static ssize_t attribute_gpio_set(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + const struct scd_gpio_attribute *gpio = to_scd_gpio_attr(devattr); + long value; + int res; + u32 reg; + + res = kstrtol(buf, 10, &value); + if (res < 0) + return res; + + if (value != 0 && value != 1) + return -EINVAL; + + reg = scd_read_register(gpio->ctx->pdev, gpio->addr); + if (gpio->active_low) { + if (value) + reg &= ~(1 << gpio->bit); + else + reg |= ~(1 << gpio->bit); + } else { + if (value) + reg |= 1 << gpio->bit; + else + reg &= ~(1 << gpio->bit); + } + scd_write_register(gpio->ctx->pdev, gpio->addr, reg); + + return count; +} + +static void scd_gpio_unregister(struct scd_context *ctx, struct scd_gpio *gpio) +{ + sysfs_remove_file(&ctx->pdev->dev.kobj, &gpio->attr.dev_attr.attr); +} + +static int scd_gpio_register(struct scd_context *ctx, struct scd_gpio *gpio) +{ + int res; + + res = sysfs_create_file(&ctx->pdev->dev.kobj, &gpio->attr.dev_attr.attr); + if (res < 0) + return res; + + list_add_tail(&gpio->list, &ctx->gpio_list); + return 0; +} + +static void scd_gpio_remove_all(struct scd_context *ctx) +{ + struct scd_gpio *tmp_gpio; + struct scd_gpio *gpio; + + list_for_each_entry_safe(gpio, tmp_gpio, &ctx->gpio_list, list) { + scd_gpio_unregister(ctx, gpio); + list_del(&gpio->list); + kfree(gpio); + } +} + +static ssize_t attribute_reset_get(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + const struct scd_reset_attribute *reset = to_scd_reset_attr(devattr); + u32 reg = scd_read_register(reset->ctx->pdev, reset->addr); + u32 res = !!(reg & (1 << reset->bit)); + return sprintf(buf, "%u\n", res); +} + +// write 1 -> set, 0 -> clear +static ssize_t attribute_reset_set(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + const struct scd_reset_attribute *reset = to_scd_reset_attr(devattr); + u32 offset = RESET_SET_OFFSET; + long value; + int res; + u32 reg; + + res = kstrtol(buf, 10, &value); + if (res < 0) + return res; + + if (value != 0 && value != 1) + return -EINVAL; + + if (!value) + offset = RESET_CLEAR_OFFSET; + + reg = 1 << reset->bit; + scd_write_register(reset->ctx->pdev, reset->addr + offset, reg); + + return count; +} + +static void scd_reset_unregister(struct scd_context *ctx, struct scd_reset *reset) +{ + sysfs_remove_file(&ctx->pdev->dev.kobj, &reset->attr.dev_attr.attr); +} + +static int scd_reset_register(struct scd_context *ctx, struct scd_reset *reset) +{ + int res; + + res = sysfs_create_file(&ctx->pdev->dev.kobj, &reset->attr.dev_attr.attr); + if (res < 0) + return res; + + list_add_tail(&reset->list, &ctx->reset_list); + return 0; +} + +static void scd_reset_remove_all(struct scd_context *ctx) +{ + struct scd_reset *tmp_reset; + struct scd_reset *reset; + + list_for_each_entry_safe(reset, tmp_reset, &ctx->reset_list, list) { + scd_reset_unregister(ctx, reset); + list_del(&reset->list); + kfree(reset); + } +} + +struct gpio_cfg { + u32 bitpos; + bool readonly; + bool active_low; + const char *name; +}; + +static int scd_xcvr_add(struct scd_context *ctx, const char *prefix, + const struct gpio_cfg *cfgs, size_t gpio_count, + u32 addr, u32 id) +{ + int i; + int err; + const struct gpio_cfg *cfg; + struct scd_gpio *gpio; + + for (i = 0; i < gpio_count; ++i) { + cfg = &cfgs[i]; + + gpio = kzalloc(sizeof(*gpio), GFP_KERNEL); + if (!gpio) { + err = -ENOMEM; + goto fail; + } + + snprintf(gpio->name, FIELD_SIZEOF(typeof(*gpio), name), + "%s%u_%s", prefix, id, cfg->name); + + if (cfg->readonly) + gpio->attr = (struct scd_gpio_attribute)SCD_RO_GPIO_ATTR( + gpio->name, ctx, addr, cfg->bitpos, cfg->active_low); + else + gpio->attr = (struct scd_gpio_attribute)SCD_RW_GPIO_ATTR( + gpio->name, ctx, addr, cfg->bitpos, cfg->active_low); + + err = scd_gpio_register(ctx, gpio); + if (err) { + kfree(gpio); + goto fail; + } + } + + return 0; + +fail: + if (gpio) + kfree(gpio); + + while (i--) { + gpio = list_last_entry(&ctx->gpio_list, struct scd_gpio, list); + if (gpio) + list_del(&gpio->list); + } + + return err; +} + +static int scd_xcvr_sfp_add(struct scd_context *ctx, u32 addr, u32 id) +{ + static const struct gpio_cfg sfp_gpios[] = { + {0, true, false, "rxlos"}, + {1, true, false, "txfault"}, + {2, true, true, "present"}, + {3, true, false, "rxlos_changed"}, + {4, true, false, "txfault_changed"}, + {5, true, false, "present_changed"}, + {6, false, false, "txdisable"}, + {7, false, false, "rate_select0"}, + {8, false, false, "rate_select1"}, + }; + + scd_dbg("sfp %u @ 0x%04x\n", id, addr); + return scd_xcvr_add(ctx, "sfp", sfp_gpios, ARRAY_SIZE(sfp_gpios), addr, id); +} + +static int scd_xcvr_qsfp_add(struct scd_context *ctx, u32 addr, u32 id) +{ + static const struct gpio_cfg qsfp_gpios[] = { + {0, true, true, "interrupt"}, + {2, true, true, "present"}, + {3, true, false, "interrupt_changed"}, + {5, true, false, "present_changed"}, + {6, false, false, "lp_mode"}, + {7, false, false, "reset"}, + {8, false, true, "modsel"}, + }; + + scd_dbg("qsfp %u @ 0x%04x\n", id, addr); + return scd_xcvr_add(ctx, "qsfp", qsfp_gpios, ARRAY_SIZE(qsfp_gpios), addr, id); +} + +static int scd_gpio_add(struct scd_context *ctx, const char *name, + u32 addr, u32 bitpos, bool read_only, bool active_low) +{ + int err; + struct scd_gpio *gpio; + + gpio = kzalloc(sizeof(*gpio), GFP_KERNEL); + if (!gpio) { + return -ENOMEM; + } + + snprintf(gpio->name, FIELD_SIZEOF(typeof(*gpio), name), name); + if (read_only) + gpio->attr = (struct scd_gpio_attribute)SCD_RO_GPIO_ATTR( + gpio->name, ctx, addr, bitpos, active_low); + else + gpio->attr = (struct scd_gpio_attribute)SCD_RW_GPIO_ATTR( + gpio->name, ctx, addr, bitpos, active_low); + + err = scd_gpio_register(ctx, gpio); + if (err) { + kfree(gpio); + return err; + } + + return 0; +} + +static int scd_reset_add(struct scd_context *ctx, const char *name, + u32 addr, u32 bitpos) +{ + int err; + struct scd_reset *reset; + + reset = kzalloc(sizeof(*reset), GFP_KERNEL); + if (!reset) { + return -ENOMEM; + } + + snprintf(reset->name, FIELD_SIZEOF(typeof(*reset), name), name); + reset->attr = (struct scd_reset_attribute)SCD_RESET_ATTR( + reset->name, ctx, addr, bitpos); + + err = scd_reset_register(ctx, reset); + if (err) { + kfree(reset); + return err; + } + + return 0; +} + +#define PARSE_INT_OR_RETURN(Buf, Tmp, Type, Ptr) \ + do { \ + int ___ret = 0; \ + Tmp = strsep(Buf, " "); \ + if (!Tmp || !*Tmp) { \ + return -EINVAL; \ + } \ + ___ret = kstrto##Type(Tmp, 0, Ptr); \ + if (___ret) { \ + return ___ret; \ + } \ + } while(0) + +#define PARSE_ADDR_OR_RETURN(Buf, Tmp, Type, Ptr, Size) \ + do { \ + PARSE_INT_OR_RETURN(Buf, Tmp, Type, Ptr); \ + if (*(Ptr) > (Size)) { \ + return -EINVAL; \ + } \ + } while(0) + +#define PARSE_STR_OR_RETURN(Buf, Tmp, Ptr) \ + do { \ + Tmp = strsep(Buf, " "); \ + if (!Tmp || !*Tmp) { \ + return -EINVAL; \ + } \ + Ptr = Tmp; \ + } while(0) + +#define PARSE_END_OR_RETURN(Buf, Tmp) \ + do { \ + Tmp = strsep(Buf, " "); \ + if (Tmp) { \ + return -EINVAL; \ + } \ + } while(0) + + +// new_master +static ssize_t parse_new_object_master(struct scd_context *ctx, + char *buf, size_t count) +{ + u32 id; + u32 addr; + u32 bus_count = MASTER_DEFAULT_BUS_COUNT; + + const char *tmp; + int res; + + if (!buf) + return -EINVAL; + + PARSE_ADDR_OR_RETURN(&buf, tmp, u32, &addr, ctx->res_size); + PARSE_INT_OR_RETURN(&buf, tmp, u32, &id); + + tmp = strsep(&buf, " "); + if (tmp && *tmp) { + res = kstrtou32(tmp, 0, &bus_count); + if (res) + return res; + PARSE_END_OR_RETURN(&buf, tmp); + } + + res = scd_smbus_master_add(ctx, addr, id, bus_count); + if (res) + return res; + + return count; +} + +// new_led +static ssize_t parse_new_object_led(struct scd_context *ctx, + char *buf, size_t count) +{ + u32 addr; + const char *name; + + const char *tmp; + int res; + + if (!buf) + return -EINVAL; + + PARSE_ADDR_OR_RETURN(&buf, tmp, u32, &addr, ctx->res_size); + PARSE_STR_OR_RETURN(&buf, tmp, name); + PARSE_END_OR_RETURN(&buf, tmp); + + res = scd_led_add(ctx, name, addr); + if (res) + return res; + + return count; +} + +enum xcvr_type { + XCVR_TYPE_SFP, + XCVR_TYPE_QSFP +}; + +static ssize_t parse_new_object_xcvr(struct scd_context *ctx, enum xcvr_type type, + char *buf, size_t count) +{ + u32 addr; + u32 id; + + const char *tmp; + int res; + + if (!buf) + return -EINVAL; + + PARSE_ADDR_OR_RETURN(&buf, tmp, u32, &addr, ctx->res_size); + PARSE_INT_OR_RETURN(&buf, tmp, u32, &id); + PARSE_END_OR_RETURN(&buf, tmp); + + if (type == XCVR_TYPE_SFP) + res = scd_xcvr_sfp_add(ctx, addr, id); + else if (type == XCVR_TYPE_QSFP) + res = scd_xcvr_qsfp_add(ctx, addr, id); + else + res = -EINVAL; + + if (res) + return res; + + return count; +} + +// new_qsfp +static ssize_t parse_new_object_qsfp(struct scd_context *ctx, + char *buf, size_t count) +{ + return parse_new_object_xcvr(ctx, XCVR_TYPE_QSFP, buf, count); +} + +// new_sfp +static ssize_t parse_new_object_sfp(struct scd_context *ctx, + char *buf, size_t count) +{ + return parse_new_object_xcvr(ctx, XCVR_TYPE_SFP, buf, count); +} + +// new_reset +static ssize_t parse_new_object_reset(struct scd_context *ctx, + char *buf, size_t count) +{ + u32 addr; + const char *name; + u32 bitpos; + + const char *tmp; + int res; + + if (!buf) + return -EINVAL; + + PARSE_ADDR_OR_RETURN(&buf, tmp, u32, &addr, ctx->res_size); + PARSE_STR_OR_RETURN(&buf, tmp, name); + PARSE_INT_OR_RETURN(&buf, tmp, u32, &bitpos); + PARSE_END_OR_RETURN(&buf, tmp); + + res = scd_reset_add(ctx, name, addr, bitpos); + if (res) + return res; + + return count; +} + +// new_gpio +static ssize_t parse_new_object_gpio(struct scd_context *ctx, + char *buf, size_t count) +{ + u32 addr; + const char *name; + u32 bitpos; + u32 read_only; + u32 active_low; + + const char *tmp; + int res; + + if (!buf) + return -EINVAL; + + PARSE_ADDR_OR_RETURN(&buf, tmp, u32, &addr, ctx->res_size); + PARSE_STR_OR_RETURN(&buf, tmp, name); + PARSE_INT_OR_RETURN(&buf, tmp, u32, &bitpos); + PARSE_INT_OR_RETURN(&buf, tmp, u32, &read_only); + PARSE_INT_OR_RETURN(&buf, tmp, u32, &active_low); + PARSE_END_OR_RETURN(&buf, tmp); + + res = scd_gpio_add(ctx, name, addr, bitpos, read_only, active_low); + if (res) + return res; + + return count; +} + +typedef ssize_t (*new_object_parse_func)(struct scd_context*, char*, size_t); +static struct { + const char *name; + new_object_parse_func func; +} funcs[] = { + { "master", parse_new_object_master }, + { "led", parse_new_object_led }, + { "qsfp", parse_new_object_qsfp }, + { "sfp", parse_new_object_sfp }, + { "reset", parse_new_object_reset }, + { "gpio", parse_new_object_gpio }, + { NULL, NULL } +}; + +static ssize_t parse_new_object(struct scd_context *ctx, const char *buf, + size_t count) +{ + char tmp[MAX_CONFIG_LINE_SIZE]; + char *ptr = tmp; + char *tok; + int i = 0; + ssize_t err; + + if (count >= MAX_CONFIG_LINE_SIZE) { + scd_warn("new_object line is too long\n"); + return -EINVAL; + } + + strncpy(tmp, buf, count); + tmp[count] = 0; + tok = strsep(&ptr, " "); + if (!tok) + return -EINVAL; + + while (funcs[i].name) { + if (!strcmp(tok, funcs[i].name)) + break; + i++; + } + + if (!funcs[i].name) + return -EINVAL; + + err = funcs[i].func(ctx, ptr, count - (ptr - tmp)); + if (err < 0) + return err; + + return count; +} + +typedef ssize_t (*line_parser_func)(struct scd_context *ctx, const char *buf, + size_t count); + +static ssize_t parse_lines(struct scd_context *ctx, const char *buf, + size_t count, line_parser_func parser) +{ + ssize_t res; + size_t left = count; + const char *nl; + + if (count == 0) + return 0; + + while (true) { + nl = strnchr(buf, left, '\n'); + if (!nl) + nl = buf + left; // points on the \0 + + res = parser(ctx, buf, nl - buf); + if (res < 0) + return res; + left -= res; + + buf = nl; + while (left && *buf == '\n') { + buf++; + left--; + } + if (!left) + break; + } + + return count; +} + +static ssize_t new_object(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t res; + struct scd_context *ctx = get_context_for_dev(dev); + + if (!ctx) { + return -ENODEV; + } + + scd_lock(ctx); + if (ctx->initialized) { + scd_unlock(ctx); + return -EBUSY; + } + res = parse_lines(ctx, buf, count, parse_new_object); + scd_unlock(ctx); + return res; +} + +static DEVICE_ATTR(new_object, S_IRUGO|S_IWUSR|S_IWGRP, 0, new_object); + +static struct scd_bus *find_scd_bus(struct scd_context *ctx, u16 bus) { + struct scd_master *master; + struct scd_bus *scd_bus; + + list_for_each_entry(master, &ctx->master_list, list) { + list_for_each_entry(scd_bus, &master->bus_list, list) { + if (scd_bus->adap.nr != bus) + continue; + return scd_bus; + } + } + return NULL; +} + +static ssize_t set_bus_params(struct scd_context *ctx, u16 bus, + struct bus_params *params) { + struct bus_params *p; + struct scd_bus *scd_bus = find_scd_bus(ctx, bus); + + if (!scd_bus) { + scd_err("Cannot find bus %d to add tweak\n", bus); + return -EINVAL; + } + + list_for_each_entry(p, &scd_bus->params, list) { + if (p->addr == params->addr) { + p->t = params->t; + p->datw = params->datw; + p->datr = params->datr; + return 0; + } + } + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + return -ENOMEM; + } + + p->addr = params->addr; + p->t = params->t; + p->datw = params->datw; + p->datr = params->datr; + list_add_tail(&p->list, &scd_bus->params); + return 0; +} + +static ssize_t parse_smbus_tweak(struct scd_context *ctx, const char *buf, + size_t count) +{ + char buf_copy[MAX_CONFIG_LINE_SIZE]; + struct bus_params params; + ssize_t err; + char *ptr = buf_copy; + const char *tmp; + u16 bus; + + if (count >= MAX_CONFIG_LINE_SIZE) { + scd_warn("smbus_tweak line is too long\n"); + return -EINVAL; + } + + strncpy(buf_copy, buf, count); + buf_copy[count] = 0; + + PARSE_INT_OR_RETURN(&ptr, tmp, u16, &bus); + PARSE_INT_OR_RETURN(&ptr, tmp, u16, ¶ms.addr); + PARSE_INT_OR_RETURN(&ptr, tmp, u8, ¶ms.t); + PARSE_INT_OR_RETURN(&ptr, tmp, u8, ¶ms.datr); + PARSE_INT_OR_RETURN(&ptr, tmp, u8, ¶ms.datw); + + err = set_bus_params(ctx, bus, ¶ms); + if (err == 0) + return count; + return err; +} + +static ssize_t smbus_tweaks(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t res; + struct scd_context *ctx = get_context_for_dev(dev); + + if (!ctx) { + return -ENODEV; + } + + scd_lock(ctx); + res = parse_lines(ctx, buf, count, parse_smbus_tweak); + scd_unlock(ctx); + return res; +} + +static DEVICE_ATTR(smbus_tweaks, S_IRUGO|S_IWUSR|S_IWGRP, 0, smbus_tweaks); + +static int scd_ext_hwmon_probe(struct pci_dev *pdev) +{ + struct scd_context *ctx = get_context_for_pdev(pdev); + int err; + + if (ctx) { + scd_warn("this pci device has already been probed\n"); + return -EEXIST; + } + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + return -ENOMEM; + } + + ctx->pdev = pdev; + get_device(&pdev->dev); + INIT_LIST_HEAD(&ctx->list); + + ctx->initialized = false; + mutex_init(&ctx->mutex); + + ctx->res_size = scd_resource_len(pdev); + + INIT_LIST_HEAD(&ctx->led_list); + INIT_LIST_HEAD(&ctx->master_list); + INIT_LIST_HEAD(&ctx->gpio_list); + INIT_LIST_HEAD(&ctx->reset_list); + + kobject_get(&pdev->dev.kobj); + err = sysfs_create_file(&pdev->dev.kobj, &dev_attr_new_object.attr); + if (err) { + goto fail_sysfs; + } + + err = sysfs_create_file(&pdev->dev.kobj, &dev_attr_smbus_tweaks.attr); + if (err) { + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_new_object.attr); + goto fail_sysfs; + } + + module_lock(); + list_add_tail(&ctx->list, &scd_list); + module_unlock(); + + return 0; + +fail_sysfs: + kobject_put(&pdev->dev.kobj); + kfree(ctx); + put_device(&pdev->dev); + + return err; +} + +static void scd_ext_hwmon_remove(struct pci_dev *pdev) +{ + struct scd_context *ctx = get_context_for_pdev(pdev); + + if (!ctx) { + return; + } + + scd_info("removing scd components\n"); + + scd_lock(ctx); + scd_smbus_remove_all(ctx); + scd_led_remove_all(ctx); + scd_gpio_remove_all(ctx); + scd_reset_remove_all(ctx); + scd_unlock(ctx); + + module_lock(); + list_del(&ctx->list); + module_unlock(); + + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_new_object.attr); + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_smbus_tweaks.attr); + + kfree(ctx); + + kobject_put(&pdev->dev.kobj); + put_device(&pdev->dev); +} + +static int scd_ext_hwmon_init_trigger(struct pci_dev *pdev) +{ + struct scd_context *ctx = get_context_for_pdev(pdev); + + if (!ctx) { + return -ENODEV; + } + + scd_lock(ctx); + ctx->initialized = true; + scd_unlock(ctx); + return 0; +} + +static struct scd_ext_ops scd_hwmon_ops = { + .probe = scd_ext_hwmon_probe, + .remove = scd_ext_hwmon_remove, + .init_trigger = scd_ext_hwmon_init_trigger, +}; + +static int __init scd_hwmon_init(void) +{ + int err = 0; + + scd_info("loading scd hwmon driver\n"); + mutex_init(&scd_hwmon_mutex); + INIT_LIST_HEAD(&scd_list); + + err = scd_register_ext_ops(&scd_hwmon_ops); + if (err) { + scd_warn("scd_register_ext_ops failed\n"); + return err; + } + + return err; +} + +static void __exit scd_hwmon_exit(void) +{ + scd_info("unloading scd hwmon driver\n"); + scd_unregister_ext_ops(); +} + +module_init(scd_hwmon_init); +module_exit(scd_hwmon_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arista Networks"); +MODULE_DESCRIPTION("SCD component driver"); diff --git a/platform/broadcom/sonic-platform-modules-arista/src/scd-hwmon.h b/platform/broadcom/sonic-platform-modules-arista/src/scd-hwmon.h new file mode 100644 index 000000000000..e23c69871f0c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/src/scd-hwmon.h @@ -0,0 +1,15 @@ +#ifndef _LINUX_DRIVER_SCD_HWMON_H_ +#define _LINUX_DRIVER_SCD_HWMON_H_ + +#include + +#define scd_err(fmt, ...) \ + pr_err("scd-hwmon: " fmt, ##__VA_ARGS__); +#define scd_warn(fmt, ...) \ + pr_warn("scd-hwmon: " fmt, ##__VA_ARGS__); +#define scd_info(fmt, ...) \ + pr_info("scd-hwmon: " fmt, ##__VA_ARGS__); +#define scd_dbg(fmt, ...) \ + pr_debug("scd-hwmon: " fmt, ##__VA_ARGS__); + +#endif /* !_LINUX_DRIVER_SCD_HWMON_H_ */ diff --git a/platform/broadcom/sonic-platform-modules-arista/src/scd.c b/platform/broadcom/sonic-platform-modules-arista/src/scd.c new file mode 100644 index 000000000000..872cffa9b4b1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/src/scd.c @@ -0,0 +1,1697 @@ +/* + * Copyright (c) 2006-2017 Arista Networks, Inc. All rights reserved. + * Arista Networks, Inc. Confidential and Proprietary. + * + * 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, see . + */ + +/* + * SCD driver. + * + * When a PCI SCD device is detected, this driver initializes the PCI device and + * maps its memory region 0 into virtual memory, but it delays registering an + * interrupt handler for the device. Instead, it creates several device attribute + * files: + * + * interrupt_status_offset + * interrupt_mask_read_offset + * interrupt_mask_set_offset + * interrupt_mask_clear_offset + * interrupt_mask + * interrupt_mask_powerloss + * interrupt_mask_ardma + * crc_error_irq + * crc_error_panic + * ptp_high_offset + * ptp_low_offset + * msi_rearm_offset + * interrupt_irq + * power_loss + * ardma_offset + * interrupt_poll + * nmi_port_io_p + * nmi_control_reg_addr + * nmi_control_mask + * nmi_status_reg_addr + * nmi_status_mask + * nmi_gpio_status_reg_addr + * nmi_gpio_status_mask + * init_trigger + * + * which will appear in the directory /sys/devices/pciAAAA:BB/AAAA:BB:CC.D/. + * + * The first 13 are read/write. The offset within memory region 0 of the interrupt + * status and interrupt mask registers can be configured by writing their values to + * the first four attribute files. The mask of valid bits in the interrupt status + * register can be set by writing its value to the fifth file (all 'invalid' bits + * are expected always to be zero). To handle + * the CRC error interrupt (completely separate from the normal interrupt line), + * write the IRQ number to crc_error_irq. Similarly, if the device interrupt is not + * the PCI interrupt, then write the interrupt_irq file. To raise a kernel panic + * upon a crc interrupt, set crc_error_panic to 1. + * + * The values written to each of these files should be in ASCII decimal. Reading + * from any of these files will return the value last written, also in ASCII decimal. + * + * After the other files have been written, writing (anything) to the + * 'init_trigger' file will cause initialization of the driver to continue, creating + * up to 32 UIO devices (/dev/uio), one for each bit that was set in the + * interrupt_mask, registering an interrupt handler. Reading from 'init_trigger' + * returns a positive initialization error if one occurred, or 0 for success. + * After successful initialization attribute files become read only. Attempts at + * changing their values results in a warning. + * + * Each UIO device corresponds to a bit in the interrupt status/mask registers. + * Reads on one of the UIO device files will complete when an interrupt has occurred + * for that bit, at which point that bit will have been added to the interrupt mask. + * It is up to the userspace code to remove that bit from the interrupt mask when it + * has handled the interrupt and cleared the interrupt at source. + * + * NMI data is also stored per-scd. nmi_priv points to the scd_dev_priv for the + * scd responsible for registering and maintaining the nmi handler. Only + * one scd is configured to handle the nmi. Userspace code (the scd agent) is trusted + * to pick the correct scd and correctly write to the corresponding attribute files. + * nmi_priv is set when writing to the nmi_control_reg_addr file, attempts by other + * devices to change nmi_priv after this point is an error. There is no protection + * against multiple entities concurrently initializing scd attributes. + * + */ + +#include +#include +#include +#include "scd.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCD_MODULE_NAME "scd" + +#define SCD_PCI_VENDOR_ID 0x3475 +#define SCD_PCI_DEVICE_ID 0x0001 +#define SCD_BAR_REGS 0 +#define SCD_BAR_1 1 +#define SCD_NUM_IRQ_REGISTERS 8 +#define SCD_REVISION_OFFSET 0x100 +#define SCD_MAGIC 0xdeadbeef +#define SCD_UNINITIALIZED 0xffffffff + +#define AMD_PCI_VENDOR_ID 0x1022 +#define AMD_PCI_EKABINI_18F5_DEVICE_ID 0x1535 +#define AMD_PCI_STEPPEEAGLE_18F5_DEVICE_ID 0x1585 + +#define INTEL_PCI_VENDOR_ID 0x8086 +#define INTEL_PCI_BROADWELL_DEVICE_ID 0x6f76 + +#define RECONFIG_PCI_SUBSYSTEM_ID 0x14 +#define RECONFIG_STATE_BAR_VALUE 0xdeadface + +#define NUM_BITS_IN_WORD 32 + +#define ASSERT(expr) do { if (unlikely(!(expr))) \ + printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d (%s:%d)\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__,current->comm,current->pid); } while (0) + +#define INTR_POLL_INTERVAL ( HZ/10 ) + +#define IOSIZE 4 + +#ifndef _PAGE_CACHE_UC +# define _PAGE_CACHE_UC _PAGE_CACHE_MODE_UC +#endif + +typedef struct scd_irq_info_s { + unsigned long interrupt_status_offset; + unsigned long interrupt_mask_read_offset; + unsigned long interrupt_mask_set_offset; + unsigned long interrupt_mask_clear_offset; + unsigned long interrupt_mask; + unsigned long interrupt_mask_powerloss; + unsigned long interrupt_mask_ardma; + struct uio_info *uio_info[NUM_BITS_IN_WORD]; + unsigned long uio_count[NUM_BITS_IN_WORD]; + char uio_names[NUM_BITS_IN_WORD][40]; +} scd_irq_info_t; + +struct scd_dev_priv { + struct list_head list; + struct pci_dev *pdev; + void __iomem *mem; + size_t mem_len; + spinlock_t ptp_time_spinlock; + scd_irq_info_t irq_info[SCD_NUM_IRQ_REGISTERS]; + unsigned long crc_error_irq; + unsigned long crc_error_panic; + unsigned long ptp_high_offset; + unsigned long ptp_low_offset; + unsigned long ptp_offset_valid; + unsigned long msi_rearm_offset; + unsigned long interrupt_irq; + unsigned long ardma_offset; + void __iomem *localbus; + unsigned long init_error; + bool initialized; + unsigned int magic; + bool sysfs_initialized; + u32 revision; + u32 revision_error_reports; + bool is_reconfig; + unsigned long interrupt_poll; + struct timer_list intr_poll_timer; + + unsigned long interrupts; + unsigned long interrupt_claimed; + unsigned long interrupt_ardma_cnt; + unsigned long interrupt_powerloss_cnt; + const struct scd_driver_cb *driver_cb; + + int lpc_device; + int lpc_vendor; + + // SCD watchdog NMI delivered through GPIO + unsigned long nmi_port_io_p; + unsigned long nmi_control_reg_addr; + unsigned long *nmi_control_reg; + unsigned long nmi_control_mask; + unsigned long nmi_status_reg_addr; + unsigned long *nmi_status_reg; + unsigned long nmi_status_mask; + unsigned long nmi_gpio_status_reg_addr; + unsigned long *nmi_gpio_status_reg; + unsigned long nmi_gpio_status_mask; + bool nmi_registered; // true if this instance registered and owns the NMI +}; + +// number of times to report a revision mismatch. +#define MAX_REV_ERR_RPTS 5 + +// non zero if debug logging is enabled. +static int debug = 0; +// linked list of scd_dev_privs +static struct list_head scd_list; +// mutex and not spinlock because of kmallocs and ardma callbacks +static struct mutex scd_mutex; +// scd_dev_priv for scd running tod counter +static struct scd_dev_priv *ptp_master_priv = NULL; +// spinlock used instead of scd_mutex for reading supe scd timestamps +static spinlock_t scd_ptp_lock; +// nmi_priv points to the scd responsible for the nmi +static struct scd_dev_priv *nmi_priv = NULL; + +#define timestamped_watchdog_panic(msg) do { \ + struct timeval tv; \ + struct tm t; \ + do_gettimeofday(&tv); \ + time_to_tm(tv.tv_sec, 0, &t); \ + panic("%s (%02d:%02d:%02d)",msg, t.tm_hour, t.tm_min, t.tm_sec); \ + }while(0); + +/* prototypes */ +static int scd_probe(struct pci_dev *pdev, const struct pci_device_id *ent); +static void scd_interrupt_poll( unsigned long data ); +static void scd_remove(struct pci_dev *pdev); +static void scd_shutdown(struct pci_dev *pdev); +static void scd_mask_interrupts(struct scd_dev_priv *priv); +static void scd_lock(void); +static void scd_unlock(void); + +static void scd_lock() { + mutex_lock(&scd_mutex); +} + +static void scd_unlock() { + mutex_unlock(&scd_mutex); +} + +struct scd_driver_cb { + int (*enable) (struct pci_dev *dev); + void (*disable) (struct pci_dev *dev); +}; + +// registered ardma handlers +static struct scd_ardma_ops *scd_ardma_ops = NULL; + +// registered scd-em callbacks +static struct scd_em_ops *scd_em_ops = NULL; + +// registered extention callbacks +static struct scd_ext_ops *scd_ext_ops = NULL; + +int scd_register_ardma_ops(struct scd_ardma_ops *ops) { + struct scd_dev_priv *priv; + + ASSERT(scd_ardma_ops == NULL); + scd_ardma_ops = ops; + + // call ardma probe() for any existing scd having ardma + scd_lock(); + list_for_each_entry(priv, &scd_list, list) { + if (priv->initialized && priv->ardma_offset) { + scd_ardma_ops->probe(priv->pdev, (void*)priv->mem, + priv->ardma_offset, + priv->localbus, + priv->irq_info[0].interrupt_mask_ardma, + priv->irq_info[0].interrupt_mask_read_offset, + priv->irq_info[0].interrupt_mask_set_offset, + priv->irq_info[0].interrupt_mask_clear_offset); + } + } + scd_unlock(); + return (0); +} + +void scd_unregister_ardma_ops() { + struct scd_dev_priv *priv; + + if (!scd_ardma_ops) { + return; + } + + // call ardma remove() for any existing scd having ardma + scd_lock(); + list_for_each_entry(priv, &scd_list, list) { + if (priv->initialized && priv->ardma_offset) { + scd_ardma_ops->remove(priv->pdev); + } + } + scd_unlock(); + scd_ardma_ops = NULL; +} + +EXPORT_SYMBOL(scd_register_ardma_ops); +EXPORT_SYMBOL(scd_unregister_ardma_ops); + +int scd_register_em_ops(struct scd_em_ops *ops) { + struct scd_dev_priv *priv; + + ASSERT(scd_em_ops == NULL); + scd_em_ops = ops; + + // call probe() for any existing scd + scd_lock(); + list_for_each_entry(priv, &scd_list, list) { + if (priv->initialized && scd_em_ops->probe) { + scd_em_ops->probe(priv->pdev); + } + } + scd_unlock(); + return (0); +} + +void scd_unregister_em_ops() { + struct scd_dev_priv *priv; + + if (!scd_em_ops) { + return; + } + + // call remove() for any existing scd + scd_lock(); + list_for_each_entry(priv, &scd_list, list) { + if (priv->initialized && scd_em_ops->remove) { + scd_em_ops->remove(priv->pdev); + } + } + scd_unlock(); + scd_em_ops = NULL; +} + +EXPORT_SYMBOL(scd_register_em_ops); +EXPORT_SYMBOL(scd_unregister_em_ops); + +int scd_register_ext_ops(struct scd_ext_ops *ops) { + struct scd_dev_priv *priv; + + ASSERT(scd_ext_ops == NULL); + scd_ext_ops = ops; + + // call probe() for any existing scd + scd_lock(); + list_for_each_entry(priv, &scd_list, list) { + if (scd_ext_ops->probe) { + scd_ext_ops->probe(priv->pdev); + } + } + scd_unlock(); + return (0); +} + +void scd_unregister_ext_ops() { + struct scd_dev_priv *priv; + + if (!scd_ext_ops) { + return; + } + + // call remove() for any existing scd + scd_lock(); + list_for_each_entry(priv, &scd_list, list) { + if (scd_ext_ops->remove) { + scd_ext_ops->remove(priv->pdev); + } + } + scd_unlock(); + scd_ext_ops = NULL; +} + +void scd_ext_init_trigger(void){ + struct scd_dev_priv *priv; + + if (!scd_ext_ops) { + return; + } + + // call init_trigger() for any existing scd + scd_lock(); + list_for_each_entry(priv, &scd_list, list) { + if (scd_ext_ops->init_trigger) { + scd_ext_ops->init_trigger(priv->pdev); + } + } + scd_unlock(); + return; +} + +EXPORT_SYMBOL(scd_register_ext_ops); +EXPORT_SYMBOL(scd_unregister_ext_ops); +EXPORT_SYMBOL(scd_ext_init_trigger); + +static irqreturn_t scd_interrupt(int irq, void *dev_id) +{ + struct device *dev = (struct device *) dev_id; + struct scd_dev_priv *priv = dev_get_drvdata(dev); + u32 interrupt_status; + u32 interrupt_mask; + u32 unmasked_interrupt_status; + irqreturn_t rc = IRQ_NONE; + u32 irq_reg; + u32 unexpected; + u32 scd_ver; + + WARN_ON_ONCE( priv->magic != SCD_MAGIC ); + + priv->interrupts++; + for(irq_reg = 0; irq_reg < SCD_NUM_IRQ_REGISTERS; irq_reg++) { + if( !priv->irq_info[irq_reg].interrupt_status_offset ) { + continue; + } + + scd_ver = ioread32( priv->mem + SCD_REVISION_OFFSET ); + if( scd_ver != priv->revision ) { + // sanity check to make sure we are not trying to read + // a scd that has just been hot removed before we + // have been notified. + // There is a problem with linecard hot removal, the power + // fails very slowly (10ms) and something manages to generate + // an interrupt, by the time we read the registers during the + // 10ms power down we can get garbage back, not just all Fs + // as you would expect from a powered down device. + if( scd_ver != 0xffffffff && + priv->revision_error_reports < MAX_REV_ERR_RPTS ) { + // Update the revision for a reconfigurable fpga. BAR0 and BAR1 will + // initially read 0xdeadface until being reconfigured. After reconfig + // BAR0 will function as a scd and return the correct version number. + if(priv->is_reconfig && priv->revision == RECONFIG_STATE_BAR_VALUE) { + priv->revision = scd_ver; + } else { + // we got garbage, this is bad so let someone know about it + dev_info( dev, "scd: irq chk 0x%x!=0x%x\n", + priv->revision, scd_ver ); + priv->revision_error_reports++; + if(priv->revision_error_reports == MAX_REV_ERR_RPTS) { + // there appear to be some cases where the kernel gets + // very confused with the scd, the physical devices seem + // to have been switched association with the kernel device + // structures, dump out the name of one of the uio, it should allow + // us to see if the device structure has become confused. + dev_err(dev, "scd: rev mismatch overflow, uio[0]:%s\n", + pci_name(priv->pdev)); + } + } + } + break; + } + + + unexpected = 0; + interrupt_status = ioread32(priv->mem + + priv->irq_info[irq_reg].interrupt_status_offset); + interrupt_mask = ioread32(priv->mem + + priv->irq_info[irq_reg].interrupt_mask_read_offset); + unmasked_interrupt_status = interrupt_status & ~interrupt_mask; + + if(debug) { + dev_info(dev, "interrupt status 0x%x interrupt mask 0x%x " + "interrupt status offset 0x%lx interrupt ", + interrupt_status, interrupt_mask, + priv->irq_info[irq_reg].interrupt_status_offset ); + } + + if (!unmasked_interrupt_status) { + /* No unmasked interrupt bits are active. Therefore the interrupt didn't + * originate from the SCD. */ + continue; + } + + /* see if this is an powerLoss interrupt + this is to speed up the handling, we don't have much time if it is a + real power loss */ + if(priv->irq_info[irq_reg].interrupt_mask_powerloss & + unmasked_interrupt_status) { + // it is the end of the line for this run, if this really is a power loss + // we will never complete the printk before the power dies + printk( KERN_INFO "Power Loss detected\n"); + priv->interrupt_powerloss_cnt++; + } + + rc = IRQ_HANDLED; + priv->interrupt_claimed++; + /* Mask all active interrupt bits. Note that we must only mask the bits that + * were not already masked when we read the interrupt mask register above. + * Otherwise, we may mask a bit that has subsequently been cleared by a process + * running on another CPU, without generating another UIO event for that bit, + * causing that process to get stuck waiting for an interrupt that will never + * arrive. */ + iowrite32(unmasked_interrupt_status, priv->mem + + priv->irq_info[irq_reg].interrupt_mask_set_offset); + + // ardma interrupt + if ((unmasked_interrupt_status & priv->irq_info[irq_reg].interrupt_mask_ardma) + && scd_ardma_ops) { + scd_ardma_ops->interrupt(priv->pdev); + unmasked_interrupt_status &= ~priv->irq_info[irq_reg].interrupt_mask_ardma; + priv->interrupt_ardma_cnt++; + } + + /* Notify the UIO layer for each of the newly active interrupt bits. */ + while (unmasked_interrupt_status) { + int bit = ffs(unmasked_interrupt_status) - 1; + if (likely(priv->irq_info[irq_reg].uio_info[bit])) { + uio_event_notify(priv->irq_info[irq_reg].uio_info[bit]); + priv->irq_info[irq_reg].uio_count[bit]++; + } else { + unexpected |= 1 << bit; + } + unmasked_interrupt_status ^= (1 << bit); + } + + if( unexpected ) { + dev_info(dev, "interrupt occurred for unexpected bits 0x%x " + "interrupt_status 0x%x, interrupt_mask 0x%x" + "scd_rev 0x%x interrupt status offset 0x%lx" + "uio mask is 0x%lx\n" , + unexpected, interrupt_status, + interrupt_mask, scd_ver, + priv->irq_info[irq_reg].interrupt_status_offset, + priv->irq_info[irq_reg].interrupt_mask ); + } + } + + /* If using MSI rearm message generation */ + if (priv->msi_rearm_offset) { + iowrite32(1, priv->mem + priv->msi_rearm_offset); + } + + return rc; +} + +static irqreturn_t scd_crc_error_interrupt(int irq, void *dev_id) +{ + struct device *dev = (struct device *) dev_id; + struct scd_dev_priv *priv = dev_get_drvdata(dev); + dev_emerg(dev, "scd: CRC error interrupt occurred!\n"); + + if (priv->initialized && priv->crc_error_panic == 1) { + /* The scd crc error irq is currently NOT shared on any platform. + * The irq source is not cleared to ensure that the capture kernel + * is not interrupted by a corrupt scd. + */ + panic( "scd_crc_error detected, system will reboot.\n" ); + } + + return IRQ_HANDLED; +} + +static int scd_finish_init(struct device *dev) +{ + struct scd_dev_priv *priv = dev_get_drvdata(dev); + int err; + int i; + unsigned int irq; + u32 irq_reg; + u32 scd_ver; + + dev_info(dev, "scd_finish_init\n"); + + // store a copy of the dev->name() for debugging hotswap + for(irq_reg = 0; irq_reg < SCD_NUM_IRQ_REGISTERS; irq_reg++) { + // combine the power loss, and regular interrupt masks + unsigned long interrupt_mask = priv->irq_info[irq_reg].interrupt_mask; + + interrupt_mask |= priv->irq_info[irq_reg].interrupt_mask_powerloss; + for (i = 0; i < NUM_BITS_IN_WORD; i++) { + priv->irq_info[irq_reg].uio_info[i] = NULL; + if (interrupt_mask & (1 << i)) { + priv->irq_info[irq_reg].uio_info[i] = + kzalloc(sizeof(struct uio_info), GFP_KERNEL); + if (!priv->irq_info[irq_reg].uio_info[i]) { + dev_err(dev, "failed to allocate UIO device\n"); + err = -ENOMEM; + goto err_out; + } + snprintf(priv->irq_info[irq_reg].uio_names[i], + sizeof(priv->irq_info[irq_reg].uio_names[i]), + "uio-%s-%d-%d", pci_name(to_pci_dev(dev)), irq_reg, i); + priv->irq_info[irq_reg].uio_info[i]->name = + priv->irq_info[irq_reg].uio_names[i]; + priv->irq_info[irq_reg].uio_info[i]->version = "0.0.1"; + priv->irq_info[irq_reg].uio_info[i]->irq = UIO_IRQ_CUSTOM; + + err = uio_register_device(dev, priv->irq_info[irq_reg].uio_info[i]); + if (err) { + dev_err(dev, "failed to register UIO device (%d)\n", err); + kfree(priv->irq_info[irq_reg].uio_info[i]); + priv->irq_info[irq_reg].uio_info[i] = NULL; + goto err_out; + } + } + } + } + + if (priv->msi_rearm_offset) { + err = pci_enable_msi(to_pci_dev(dev)); + if (err) { + dev_err(dev, "failed to enable msi (%d)\n", err); + goto err_out; + } + pci_set_master(to_pci_dev(dev)); + } + + /* if interrupt_irq has been set, use it instead of pdev->irq */ + irq = (priv->interrupt_irq != SCD_UNINITIALIZED) ? + priv->interrupt_irq : to_pci_dev(dev)->irq; + + err = request_irq(irq, scd_interrupt, IRQF_SHARED, SCD_MODULE_NAME, dev); + if (err) { + dev_err(dev, "failed to request irq %d (%d)\n", irq, err); + goto err_out_misc_dereg; + } + + if (priv->crc_error_irq != SCD_UNINITIALIZED) { + err = request_irq(priv->crc_error_irq, scd_crc_error_interrupt, 0, + SCD_MODULE_NAME, dev); + if (err) { + dev_err(dev, "failed to request CRC error IRQ %lu (%d)\n", + priv->crc_error_irq, err); + goto err_out_free_irq; + } + } + + // enable power loss interrupts + for(irq_reg = 0; irq_reg < SCD_NUM_IRQ_REGISTERS; irq_reg++) { + if(priv->irq_info[irq_reg].interrupt_mask_powerloss && + priv->irq_info[irq_reg].interrupt_mask_clear_offset) { + iowrite32(priv->irq_info[irq_reg].interrupt_mask_powerloss, priv->mem + + priv->irq_info[irq_reg].interrupt_mask_clear_offset); + } + } + + // ardma probe + if (priv->ardma_offset && scd_ardma_ops) { + scd_ardma_ops->probe(priv->pdev, (void*)priv->mem, + priv->ardma_offset, + priv->localbus, + priv->irq_info[0].interrupt_mask_ardma, + priv->irq_info[0].interrupt_mask_read_offset, + priv->irq_info[0].interrupt_mask_set_offset, + priv->irq_info[0].interrupt_mask_clear_offset); + } + + // scd_em probe + if (scd_em_ops && scd_em_ops->probe) { + scd_em_ops->probe(priv->pdev); + } + + // scd_ext init_trigger + if (scd_ext_ops && scd_ext_ops->init_trigger) { + scd_ext_ops->init_trigger(priv->pdev); + } + + // interrupt polling + if( priv->interrupt_poll != SCD_UNINITIALIZED ) { + setup_timer( &priv->intr_poll_timer, scd_interrupt_poll, (unsigned long)priv ); + priv->intr_poll_timer.expires = jiffies + INTR_POLL_INTERVAL; + add_timer( &priv->intr_poll_timer ); + } + + // If using MSI rearm message generation + if (priv->msi_rearm_offset) { + iowrite32(1, priv->mem + priv->msi_rearm_offset); + } + + // verify that the scd is actually programmed by performing a sanity check + // on the revision register + scd_ver = ioread32( priv->mem + SCD_REVISION_OFFSET ); + if (scd_ver == SCD_UNINITIALIZED) { + dev_err(dev, "scd is not programmed\n"); + err = -ENODEV; + goto err_out_free_irq; + } + + dev_info(dev, "scd device initialization complete\n"); + return 0; + +err_out_free_irq: + free_irq(irq, dev); + +err_out_misc_dereg: + if (priv->msi_rearm_offset) { + pci_disable_msi(to_pci_dev(dev)); + } + +err_out: + for(irq_reg = 0; irq_reg < SCD_NUM_IRQ_REGISTERS; irq_reg++) { + for (i = 0; i < NUM_BITS_IN_WORD; i++) { + if (priv->irq_info[irq_reg].uio_info[i]) { + uio_unregister_device(priv->irq_info[irq_reg].uio_info[i]); + kfree(priv->irq_info[irq_reg].uio_info[i]); + priv->irq_info[irq_reg].uio_info[i] = NULL; + } + } + } + dev_err( dev, "scd device initialization failed with error %d", err ); + return err; +} + +static ssize_t show_attr(struct scd_dev_priv *priv, unsigned long *value, char *buf) +{ + ssize_t ret; + scd_lock(); + ret = sprintf(buf, "%lu\n", *value); + scd_unlock(); + return ret; +} + +static ssize_t store_attr(struct device *dev, const char *name, + unsigned long *value, const char *buf, size_t count) +{ + struct scd_dev_priv *priv = dev_get_drvdata(dev); + unsigned long new_value = simple_strtoul(buf, NULL, 10); + scd_lock(); + if (!priv->initialized) { + *value = new_value; + } else if (new_value != *value) { + dev_warn(dev, "attempt to change %s after device initialized\n", name); + } + scd_unlock(); + return count; +} + +#define SCD_DEVICE_ATTR(_name) \ +static ssize_t show_##_name(struct device *dev, struct device_attribute *attr, \ +char *buf) \ +{ \ + struct scd_dev_priv *priv = dev_get_drvdata(dev); \ + return show_attr(priv, &priv->_name, buf); \ +} \ +static ssize_t store_##_name(struct device *dev, struct device_attribute *attr, \ +const char *buf, size_t count) \ +{ \ + struct scd_dev_priv *priv = dev_get_drvdata(dev); \ + return store_attr(dev, #_name, &priv->_name, buf, count); \ +} \ +static DEVICE_ATTR(_name, S_IRUGO|S_IWUSR|S_IWGRP, show_##_name, store_##_name); + + +#define SCD_IRQ_DEVICE_ATTR(_name, _num) \ +static ssize_t show_##_name##_num(struct device *dev, struct device_attribute *attr,\ + char *buf) \ +{ \ + struct scd_dev_priv *priv = dev_get_drvdata(dev); \ + return show_attr(priv, &priv->irq_info[_num]._name, buf); \ +} \ +static ssize_t store_##_name##_num(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct scd_dev_priv *priv = dev_get_drvdata(dev); \ + return store_attr(dev, #_name #_num, &priv->irq_info[_num]._name, buf, count); \ +} \ +static DEVICE_ATTR(_name##_num, S_IRUGO|S_IWUSR|S_IWGRP, show_##_name##_num, \ + store_##_name##_num); + +#define SCD_IRQ_ATTRS(num) \ +SCD_IRQ_DEVICE_ATTR(interrupt_status_offset, num); \ +SCD_IRQ_DEVICE_ATTR(interrupt_mask_read_offset, num); \ +SCD_IRQ_DEVICE_ATTR(interrupt_mask_set_offset, num); \ +SCD_IRQ_DEVICE_ATTR(interrupt_mask_clear_offset, num); \ +SCD_IRQ_DEVICE_ATTR(interrupt_mask, num); \ +SCD_IRQ_DEVICE_ATTR(interrupt_mask_powerloss, num); \ +SCD_IRQ_DEVICE_ATTR(interrupt_mask_ardma, num); + +#define SCD_IRQ_ATTRS_POINTERS(num) \ +&dev_attr_interrupt_status_offset##num.attr, \ +&dev_attr_interrupt_mask_read_offset##num.attr, \ +&dev_attr_interrupt_mask_set_offset##num.attr, \ +&dev_attr_interrupt_mask_clear_offset##num.attr, \ +&dev_attr_interrupt_mask##num.attr, \ +&dev_attr_interrupt_mask_powerloss##num.attr, \ +&dev_attr_interrupt_mask_ardma##num.attr + +struct pci_dev * +scd_get_pdev(const char *name) +{ + struct scd_dev_priv *priv = NULL; + + scd_lock(); + list_for_each_entry(priv, &scd_list, list) { + if (!strcmp(dev_name(&priv->pdev->dev), name)) { + scd_unlock(); + return (priv->pdev); + } + } + scd_unlock(); + return (NULL); +} +EXPORT_SYMBOL(scd_get_pdev); + +u32 +scd_read_register(struct pci_dev *pdev, u32 offset) +{ + void __iomem *reg; + u32 res = 0; + struct scd_dev_priv *priv; + + priv = pci_get_drvdata(pdev); + ASSERT( priv ); + ASSERT( offset < priv->mem_len ); + if (priv) { + reg = priv->mem + offset; + res = ioread32(reg); + } + dev_dbg(&pdev->dev, "io:read 0x%04x => 0x%08x", offset, res); + return res; +} +EXPORT_SYMBOL(scd_read_register); + +void +scd_write_register(struct pci_dev *pdev, u32 offset, u32 val) +{ + void __iomem *reg; + struct scd_dev_priv *priv; + + priv = pci_get_drvdata(pdev); + ASSERT( priv ); + ASSERT( offset < priv->mem_len ); + dev_dbg(&pdev->dev, "io:write 0x%04x <= 0x%08x", offset, val); + if (priv) { + reg = priv->mem + offset; + iowrite32(val, reg); + } +} +EXPORT_SYMBOL(scd_write_register); + +size_t +scd_resource_len(struct pci_dev *pdev) +{ + struct scd_dev_priv *priv; + + priv = pci_get_drvdata(pdev); + ASSERT( priv ); + if (priv) + return priv->mem_len; + return 0; +} +EXPORT_SYMBOL(scd_resource_len); + +// scd_list_lock mutex is not held in this function. +// scd_lock mutex is not held in this function. +u64 +scd_ptp_timestamp(void) +{ + unsigned long flags, ptp_lock_flags; + u64 ts = 0; + u32 low = 0; + u32 high = 0; + struct scd_dev_priv *priv = ptp_master_priv; + + spin_lock_irqsave(&scd_ptp_lock, ptp_lock_flags); + + if (priv && priv->initialized && (priv->ptp_offset_valid != SCD_UNINITIALIZED)) { + ASSERT(priv->ptp_low_offset != SCD_UNINITIALIZED); + ASSERT(priv->ptp_high_offset != SCD_UNINITIALIZED); + // Reading the high register also latches the current time into the low + // register, so we don't need any special handling of the rollover case. + spin_lock_irqsave(&priv->ptp_time_spinlock, flags); + high = ioread32(priv->mem + priv->ptp_high_offset); + low = ioread32(priv->mem + priv->ptp_low_offset); + spin_unlock_irqrestore(&priv->ptp_time_spinlock, flags); + ts = (((u64)high) << 32) | low; + } + + spin_unlock_irqrestore(&scd_ptp_lock, ptp_lock_flags); + if (ts == 0) + printk(KERN_INFO "%s %s returned zero\n", SCD_MODULE_NAME, __FUNCTION__); + + return (ts); +} + +static ssize_t show_init_trigger(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scd_dev_priv *priv = dev_get_drvdata(dev); + return show_attr(priv, &priv->init_error, buf); +} + +static ssize_t store_init_trigger(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct scd_dev_priv *priv; + int error = 0; + + priv = dev_get_drvdata(dev); + + scd_lock(); + + // If private data is dead, return + if( priv->magic != SCD_MAGIC ) { + scd_unlock(); + return -ENODEV; + } + + if (!priv->initialized) { + if (!(error = scd_finish_init(dev))) { + priv->initialized = 1; + } + } + + // Save the error code from scd_finish_init. + // We flip this back to positive so that the conversion to unsigned won't + // produce weird, hard to read values + priv->init_error = -error; + + scd_unlock(); + + return error ? error : count; +} + +static ssize_t scd_set_debug(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { + sscanf( buf, "%d", &debug ); + return count; +} + +static ssize_t scd_set_ptp_offset_valid(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { + + struct scd_dev_priv *priv = dev_get_drvdata(dev); + unsigned long valid = simple_strtoul(buf, NULL, 10); + + scd_lock(); + if (!priv->initialized) { + priv->ptp_offset_valid = valid; + if((priv->ptp_offset_valid != SCD_UNINITIALIZED) && !ptp_master_priv) { + ptp_master_priv = priv; + } + } else if (priv->ptp_offset_valid != valid) { + dev_warn(dev, "attempt to change ptp_offset_valid after device initialized\n"); + } + scd_unlock(); + return count; +} + +static ssize_t scd_set_nmi_control_reg_addr(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { + + struct scd_dev_priv *priv = dev_get_drvdata(dev); + unsigned long value = (unsigned int)simple_strtoul(buf, NULL, 10); + scd_lock(); + if (!priv->initialized) { + if (nmi_priv && priv != nmi_priv) { + dev_err(dev, "Multiple devices attempting to set NMI attributes\n"); + scd_unlock(); + return count; + } + if (value != SCD_UNINITIALIZED && !nmi_priv) { + nmi_priv = priv; + } + priv->nmi_control_reg_addr = value; + } else if (priv->nmi_control_reg_addr != value) { + dev_warn(dev, + "attempt to change nmi_control_reg_addr after device initialized\n"); + } + scd_unlock(); + return count; +} + +static DEVICE_ATTR(init_trigger, S_IRUGO|S_IWUSR|S_IWGRP, + show_init_trigger, store_init_trigger); +static DEVICE_ATTR(debug, S_IWUSR|S_IWGRP, NULL, scd_set_debug ); +static DEVICE_ATTR(ptp_offset_valid, S_IWUSR|S_IWGRP, + NULL, scd_set_ptp_offset_valid ); +static DEVICE_ATTR(nmi_control_reg_addr, S_IWUSR|S_IWGRP, + NULL, scd_set_nmi_control_reg_addr ); + +SCD_DEVICE_ATTR(crc_error_irq); +SCD_DEVICE_ATTR(crc_error_panic); +SCD_DEVICE_ATTR(ptp_high_offset); +SCD_DEVICE_ATTR(ptp_low_offset); +SCD_DEVICE_ATTR(msi_rearm_offset); +SCD_DEVICE_ATTR(interrupt_irq); +SCD_DEVICE_ATTR(ardma_offset); +SCD_DEVICE_ATTR(interrupt_poll); + +SCD_DEVICE_ATTR(nmi_port_io_p); +SCD_DEVICE_ATTR(nmi_control_mask); +SCD_DEVICE_ATTR(nmi_status_reg_addr); +SCD_DEVICE_ATTR(nmi_status_mask); +SCD_DEVICE_ATTR(nmi_gpio_status_reg_addr); +SCD_DEVICE_ATTR(nmi_gpio_status_mask); + +/* the number of SCD_IRQ_ATTRS() must match SCD_NUM_IRQ_REGISTERS above */ +SCD_IRQ_ATTRS(0); +SCD_IRQ_ATTRS(1); +SCD_IRQ_ATTRS(2); +SCD_IRQ_ATTRS(3); +SCD_IRQ_ATTRS(4); +SCD_IRQ_ATTRS(5); +SCD_IRQ_ATTRS(6); +SCD_IRQ_ATTRS(7); + +static struct attribute *scd_attrs[] = { + SCD_IRQ_ATTRS_POINTERS(0), + SCD_IRQ_ATTRS_POINTERS(1), + SCD_IRQ_ATTRS_POINTERS(2), + SCD_IRQ_ATTRS_POINTERS(3), + SCD_IRQ_ATTRS_POINTERS(4), + SCD_IRQ_ATTRS_POINTERS(5), + SCD_IRQ_ATTRS_POINTERS(6), + SCD_IRQ_ATTRS_POINTERS(7), + &dev_attr_crc_error_irq.attr, + &dev_attr_crc_error_panic.attr, + &dev_attr_ptp_high_offset.attr, + &dev_attr_ptp_low_offset.attr, + &dev_attr_msi_rearm_offset.attr, + &dev_attr_interrupt_irq.attr, + &dev_attr_ardma_offset.attr, + &dev_attr_init_trigger.attr, + &dev_attr_interrupt_poll.attr, + &dev_attr_debug.attr, + &dev_attr_nmi_port_io_p.attr, + &dev_attr_nmi_control_reg_addr.attr, + &dev_attr_nmi_control_mask.attr, + &dev_attr_nmi_status_reg_addr.attr, + &dev_attr_nmi_status_mask.attr, + &dev_attr_nmi_gpio_status_reg_addr.attr, + &dev_attr_nmi_gpio_status_mask.attr, + &dev_attr_ptp_offset_valid.attr, + NULL, +}; + +static struct attribute_group scd_attr_group = { + .attrs = scd_attrs, +}; + +static void scd_pci_disable(struct pci_dev *pdev) +{ + struct scd_dev_priv *priv = pci_get_drvdata(pdev); + + if (priv->mem) { + pci_iounmap(pdev, priv->mem); + pci_release_region(pdev, SCD_BAR_REGS); + priv->mem = NULL; + } + + if (priv->localbus) { + pci_iounmap(pdev, priv->localbus); + pci_release_region(pdev, SCD_BAR_1); + priv->localbus = NULL; + } + + pci_disable_device(pdev); +} + +static int +scd_pci_enable(struct pci_dev *pdev) +{ + struct scd_dev_priv *priv = pci_get_drvdata(pdev); + int err; + u16 ssid; + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "cannot enable PCI device (%d)\n", err); + goto fail; + } + + err = pci_request_region(pdev, SCD_BAR_REGS, SCD_MODULE_NAME); + if (err) { + dev_err(&pdev->dev, "cannot obtain PCI memory region (%d)\n", err); + goto fail; + } + + priv->mem = pci_iomap(pdev, SCD_BAR_REGS, 0); + if(!priv->mem) { + dev_err(&pdev->dev, "cannot remap PCI memory region\n"); + err = -ENOMEM; + goto fail; + } + + priv->mem_len = pci_resource_len(pdev, SCD_BAR_REGS); + + // check if this device uses partial reconfiguration to load the scd image + pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &ssid); + if (ssid == RECONFIG_PCI_SUBSYSTEM_ID) { + priv->is_reconfig = true; + } else { + if (pci_resource_flags(pdev, SCD_BAR_1) & IORESOURCE_MEM) { + err = pci_request_region(pdev, SCD_BAR_1, SCD_MODULE_NAME); + if (err) { + dev_err(&pdev->dev, "cannot obtain PCI memory region 1 (%d)\n", err); + goto fail; + } + priv->localbus = pci_iomap(pdev, SCD_BAR_1, 0); + if (!priv->localbus) { + dev_err(&pdev->dev, "cannot remap memory region 1\n"); + err = -ENOMEM; + goto fail; + } + } + } + fail: + return err; +} + +static const struct scd_driver_cb scd_pci_cb = { + .enable = scd_pci_enable, + .disable = scd_pci_disable, +}; + +static int scd_lpc_enable(struct pci_dev *pdev); +static void scd_lpc_disable(struct pci_dev *pdev); +static const struct scd_driver_cb scd_lpc_cb = { + .enable = scd_lpc_enable, + .disable = scd_lpc_disable, +}; + +static struct pci_device_id scd_lpc_table[] = { + { PCI_DEVICE( AMD_PCI_VENDOR_ID, AMD_PCI_EKABINI_18F5_DEVICE_ID ) }, + { PCI_DEVICE( AMD_PCI_VENDOR_ID, AMD_PCI_STEPPEEAGLE_18F5_DEVICE_ID ) }, + { PCI_DEVICE( INTEL_PCI_VENDOR_ID, INTEL_PCI_BROADWELL_DEVICE_ID ) }, + { 0 }, +}; + +// +// the LPC driver takes three parameters +// scd.lpc_res_addr - beginning of the LPC physical memory +// scd.lpc_res_size - size of the LPC block, in 4K increments +// scd.lpc_irq - assigned interrupt number +// this driver uses the LPC-ISA bridge available in the AMD-Kabini chip +// as the PCI device to export the resource0 for EOS application code to +// map. +static unsigned long lpc_res_addr; +module_param(lpc_res_addr, long, 0); +MODULE_PARM_DESC(lpc_res_addr, "physical address of LPC resource"); +static int lpc_res_size; +module_param(lpc_res_size, int, 0); +MODULE_PARM_DESC(lpc_res_size, "size of LPC resource"); +static int lpc_irq = -1; +module_param(lpc_irq, int, 0); +MODULE_PARM_DESC(lpc_irq, "interrupt of LPC SCD"); +static const struct scd_driver_cb scd_lpc_cb; + +static int scd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct scd_dev_priv *priv; + u32 fpga_rev, board_rev; + int err; + const struct scd_driver_cb *scd_cb; + + if (pci_match_id(scd_lpc_table, pdev)) { + // matched LPC device + if (!((lpc_irq >= 0) || lpc_res_addr || lpc_res_size)) { + // nothing is enabled, we are not running in LPC mode, return + return -ENODEV; + } + + if (lpc_irq < 0) { + dev_err(&pdev->dev, "Invalid LPC interrupt %d", lpc_irq); + return -EINVAL; + } + + if (!lpc_res_addr) { + dev_err(&pdev->dev, "No LPC scd address specified"); + return -EINVAL; + } + + if (!lpc_res_size) { + dev_err(&pdev->dev, "No LPC scd size specified"); + return -EINVAL; + } + scd_cb = &scd_lpc_cb; + } else { + scd_cb = &scd_pci_cb; + } + + if (pci_get_drvdata(pdev)) { + dev_warn(&pdev->dev, "private data already attached %p", + pci_get_drvdata(pdev)); + } + + priv = kmalloc(sizeof(struct scd_dev_priv), GFP_KERNEL); + if (priv == NULL) { + dev_err(&pdev->dev, "cannot allocate private data, aborting\n"); + err = -ENOMEM; + goto fail; + } + + memset(priv, 0, sizeof (struct scd_dev_priv)); + INIT_LIST_HEAD(&priv->list); + priv->pdev = pdev; + priv->crc_error_irq = SCD_UNINITIALIZED; + priv->crc_error_panic = SCD_UNINITIALIZED; + priv->interrupt_irq = SCD_UNINITIALIZED; + priv->interrupt_poll = SCD_UNINITIALIZED; + priv->ptp_high_offset = SCD_UNINITIALIZED; + priv->ptp_low_offset = SCD_UNINITIALIZED; + priv->ptp_offset_valid = SCD_UNINITIALIZED; + + priv->nmi_port_io_p = SCD_UNINITIALIZED; + priv->nmi_control_reg_addr = SCD_UNINITIALIZED; + priv->nmi_control_reg = NULL; + priv->nmi_control_mask = SCD_UNINITIALIZED; + priv->nmi_status_reg_addr = SCD_UNINITIALIZED; + priv->nmi_status_reg = NULL; + priv->nmi_status_mask = SCD_UNINITIALIZED; + priv->nmi_gpio_status_reg_addr = SCD_UNINITIALIZED; + priv->nmi_gpio_status_reg = NULL; + priv->nmi_gpio_status_mask = SCD_UNINITIALIZED; + priv->nmi_registered = false; + + spin_lock_init(&priv->ptp_time_spinlock); + priv->magic = SCD_MAGIC; + priv->localbus = NULL; + priv->driver_cb = scd_cb; + + pci_set_drvdata(pdev, priv); + + err = scd_cb->enable(pdev); + if (err) { + goto fail; + } + + err = sysfs_create_group(&pdev->dev.kobj, &scd_attr_group); + if (err) { + dev_err(&pdev->dev, "sysfs_create_group() error %d\n", err); + goto fail; + } + priv->sysfs_initialized = 1; + priv->initialized = 0; + + // add to our list + scd_lock(); + list_add_tail(&priv->list, &scd_list); + scd_unlock(); + + priv->revision = ioread32(priv->mem + SCD_REVISION_OFFSET); + fpga_rev = (priv->revision & 0xffff0000) >> 16; + board_rev = priv->revision & 0x00000fff; + + if (priv->is_reconfig && (priv->revision==RECONFIG_STATE_BAR_VALUE)) { + dev_info(&pdev->dev, "scd detected\n FPGA in reconfig state\n"); + } else { + dev_info(&pdev->dev, "scd detected\n FPGA revision %d, board revision %d\n", + fpga_rev, board_rev); + } + + return 0; + +fail: + scd_remove(pdev); + + return err; +} + +static void scd_interrupt_poll( unsigned long data ) +{ + struct scd_dev_priv * dev = ( struct scd_dev_priv * ) data; + struct pci_dev * pdev = dev->pdev; + scd_interrupt( 0, ( void* ) &pdev->dev ); + dev->intr_poll_timer.expires = jiffies + INTR_POLL_INTERVAL; + add_timer( &dev->intr_poll_timer ); +} + +static void scd_remove(struct pci_dev *pdev) +{ + struct scd_dev_priv *priv = pci_get_drvdata(pdev); + unsigned int irq; + int i; + u32 irq_reg; + + if (priv == NULL) + return; + + spin_lock(&scd_ptp_lock); + if(ptp_master_priv == priv) { + ptp_master_priv = NULL; + } + spin_unlock(&scd_ptp_lock); + + scd_lock(); + + if (priv == nmi_priv) { + if (priv->nmi_registered) { + unregister_nmi_handler(NMI_LOCAL, "WATCHDOG_NMI"); + priv->nmi_registered = false; + } + nmi_priv = NULL; + } + + // call ardma remove() if scd has ardma + if (priv->initialized && priv->ardma_offset && scd_ardma_ops) { + scd_ardma_ops->remove(pdev); + } + + // call scd_em's remove callback + if (scd_em_ops && scd_em_ops->remove) { + scd_em_ops->remove(pdev); + } + + // call scd_sonic remove callback + if (scd_ext_ops && scd_ext_ops->remove) { + scd_ext_ops->remove(pdev); + } + + //stop interrupt polling if we've initialized it + if( priv->interrupt_poll != SCD_UNINITIALIZED ) { + del_timer_sync( &priv->intr_poll_timer ); + } + + // remove from our list + list_del_init(&priv->list); + + irq = (priv->interrupt_irq != SCD_UNINITIALIZED) ? + priv->interrupt_irq : pdev->irq; + + if (priv->initialized) { + scd_mask_interrupts(priv); + free_irq(irq, &pdev->dev); + if (priv->crc_error_irq != SCD_UNINITIALIZED) + free_irq(priv->crc_error_irq, &pdev->dev); + if (priv->msi_rearm_offset) { + pci_disable_msi(pdev); + } + } + + // call pci bits to release + priv->driver_cb->disable( pdev ); + + if (priv->initialized) { + for(irq_reg = 0; irq_reg < SCD_NUM_IRQ_REGISTERS; irq_reg++) { + for (i = 0; i < NUM_BITS_IN_WORD; i++) { + if (priv->irq_info[irq_reg].uio_info[i]) { + uio_unregister_device(priv->irq_info[irq_reg].uio_info[i]); + kfree(priv->irq_info[irq_reg].uio_info[i]); + priv->irq_info[irq_reg].uio_info[i] = NULL; + } + } + } + } + priv->magic = 0; + + // release lock before removing sysfs to avoid deadlocks + scd_unlock(); + + if (priv->sysfs_initialized) { + sysfs_remove_group(&pdev->dev.kobj, &scd_attr_group); + } + + ASSERT( !priv->localbus ); + ASSERT( !priv->mem ); + if( priv->initialized ) { + for(irq_reg = 0; irq_reg < SCD_NUM_IRQ_REGISTERS; irq_reg++) { + for (i = 0; i < NUM_BITS_IN_WORD; i++) { + ASSERT( !priv->irq_info[irq_reg].uio_info[i] ); + } + } + } + + pci_set_drvdata(pdev, NULL); + memset(priv, 0, sizeof (struct scd_dev_priv)); + + kfree(priv); + + dev_info(&pdev->dev, "scd removed\n"); +} + +static void scd_shutdown(struct pci_dev *pdev) { + struct scd_dev_priv *priv = pci_get_drvdata(pdev); + dev_info(&pdev->dev, "scd shutdown\n"); + scd_mask_interrupts(priv); + + // call ardma shutdown() if scd has ardma + if (priv->initialized && priv->ardma_offset && scd_ardma_ops) { + scd_ardma_ops->shutdown(pdev); + } +} + +static void scd_mask_interrupts(struct scd_dev_priv *priv) { + u32 i; + + if (priv == NULL || !priv->initialized) { + return; + } + + for (i = 0; i < SCD_NUM_IRQ_REGISTERS; i++) { + if (priv->irq_info[i].interrupt_mask_set_offset) { + iowrite32(0xffffffff, + priv->mem + priv->irq_info[i].interrupt_mask_set_offset); + // stall until previous write completes + (void) ioread32(priv->mem + priv->irq_info[i].interrupt_mask_set_offset); + } + } +} + +static pci_ers_result_t scd_error_detected(struct pci_dev *pdev, + enum pci_channel_state state) { + dev_err(&pdev->dev, "error detected (state=%d)\n", state); + return PCI_ERS_RESULT_DISCONNECT; +} + +static struct pci_device_id scd_pci_table[] = { + { PCI_DEVICE( SCD_PCI_VENDOR_ID, SCD_PCI_DEVICE_ID ) }, + { PCI_DEVICE( AMD_PCI_VENDOR_ID, AMD_PCI_EKABINI_18F5_DEVICE_ID ) }, + { PCI_DEVICE( AMD_PCI_VENDOR_ID, AMD_PCI_STEPPEEAGLE_18F5_DEVICE_ID ) }, + { PCI_DEVICE( INTEL_PCI_VENDOR_ID, INTEL_PCI_BROADWELL_DEVICE_ID ) }, + { 0, }, +}; + +static int scd_dump(struct seq_file *m, void *p) { + struct scd_dev_priv *priv; + u32 irq_reg; + int i; + unsigned long uio_count; + + scd_lock(); + seq_printf(m, "\ndebug 0x%x\n\n", debug); + list_for_each_entry(priv, &scd_list, list) { + if(priv->magic == SCD_MAGIC) { + seq_printf(m, "scd %s\n", pci_name(priv->pdev)); + + seq_printf(m, "revision 0x%x revision_error_reports %u\n", + priv->revision, + priv->revision_error_reports); + + seq_printf(m, "initialized %d sysfs_initialized %d" + " interrupt_poll %lu magic 0x%x" + " is_reconfig %d\n", priv->initialized, + priv->sysfs_initialized, + priv->interrupt_poll, + priv->magic, + priv->is_reconfig); + + seq_printf(m, "ptp_offset_valid 0x%lx ptp_high_offset 0x%lx" + " ptp_low_offset 0x%lx ardma_offset %lu msi_rearm_offset %lu\n", + priv->ptp_offset_valid, priv->ptp_high_offset, + priv->ptp_low_offset, priv->ardma_offset, + priv->msi_rearm_offset); + + seq_printf(m, "nmi_port_io_p 0x%lx nmi_control_reg_addr 0x%lx " + "nmi_control_mask 0x%lx\nnmi_status_reg_addr 0x%lx " + "nmi_status_mask 0x%lx nmi_gpio_status_reg_addr 0x%lx\n" + "nmi_gpio_status_mask 0x%lx nmi_registered %d\n", + priv->nmi_port_io_p, priv->nmi_control_reg_addr, + priv->nmi_control_mask, priv->nmi_status_reg_addr, + priv->nmi_status_mask, priv->nmi_gpio_status_reg_addr, + priv->nmi_gpio_status_mask, priv->nmi_registered); + + for(irq_reg = 0; irq_reg < SCD_NUM_IRQ_REGISTERS; irq_reg++) { + if(!priv->irq_info[irq_reg].interrupt_status_offset || + !priv->irq_info[irq_reg].interrupt_mask_read_offset || + !priv->irq_info[irq_reg].interrupt_mask_set_offset) { + continue; + } + seq_printf(m, "interrupt register %u:\n", irq_reg); + seq_printf(m, "interrupt_status_offset 0x%lx " + "interrupt_mask_read_offset 0x%lx " + "interrupt_mask_set_offset 0x%lx\n" + "interrupt_mask_clear_offset 0x%lx " + "interrupt_mask 0x%lx " + "interrupt_mask_power_loss 0x%lx\n" + "ardma_interrupt_mask 0x%lx\n", + priv->irq_info[irq_reg].interrupt_status_offset, + priv->irq_info[irq_reg].interrupt_mask_read_offset, + priv->irq_info[irq_reg].interrupt_mask_set_offset, + priv->irq_info[irq_reg].interrupt_mask_clear_offset, + priv->irq_info[irq_reg].interrupt_mask, + priv->irq_info[irq_reg].interrupt_mask_powerloss, + priv->irq_info[irq_reg].interrupt_mask_ardma); + + } + + seq_printf(m, "irq %u\n", priv->pdev->irq ); + seq_printf(m, "interrupts %lu interrupts_claimed %lu\n", + priv->interrupts, priv->interrupt_claimed ); + + seq_printf(m, "interrupt status bit counts:\n"); + + for(irq_reg = 0; irq_reg < SCD_NUM_IRQ_REGISTERS; irq_reg++) { + if(!priv->irq_info[irq_reg].interrupt_status_offset || + !priv->irq_info[irq_reg].interrupt_mask_read_offset || + !priv->irq_info[irq_reg].interrupt_mask_set_offset) { + continue; + } + + for (i = 0; i < NUM_BITS_IN_WORD; i++) { + uio_count = priv->irq_info[irq_reg].uio_count[i]; + if(uio_count) { + seq_printf(m, "%d[%d] %lu\n", irq_reg, i, uio_count ); + } + } + + if(priv->interrupt_ardma_cnt) + seq_printf(m, "ardma interrupts %lu ", priv->interrupt_ardma_cnt); + if(priv->interrupt_powerloss_cnt) + seq_printf(m, "power loss interrupts %lu\n", + priv->interrupt_powerloss_cnt); + } + } + seq_printf(m, "\n"); + } + + scd_unlock(); + return 0; +} + +static int scd_dump_open( struct inode *inode, struct file *file ) { + return (single_open(file, scd_dump, NULL)); +} + +static const struct file_operations scd_dump_file_ops = { + .owner = THIS_MODULE, + .open = scd_dump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void scd_procfs_create( void ) { + struct proc_dir_entry *entry; + entry = proc_create( SCD_MODULE_NAME, 0, NULL, &scd_dump_file_ops ); +} + +static void scd_procfs_remove( void ) { + (void) remove_proc_entry( SCD_MODULE_NAME, NULL ); +} + +MODULE_DEVICE_TABLE(pci, scd_pci_table); + +static struct pci_error_handlers scd_error_handlers = { + .error_detected = scd_error_detected, +}; + +static struct pci_driver scd_driver = { + .name = "scd", + .id_table = scd_pci_table, + .probe = scd_probe, + .remove = scd_remove, + .err_handler = &scd_error_handlers, + .shutdown = &scd_shutdown, +}; + +static int +scd_lpc_mmap_resource(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + struct pci_dev *pdev = to_pci_dev(container_of(kobj, + struct device, kobj)); + unsigned long prot; + int rc; + + // validate range of mapping + if ((vma->vm_pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT)) > + (attr->size >> PAGE_SHIFT)) { + dev_err(&pdev->dev, "invalid vm region addr 0x%lx-0x%lx offset pages %lu\n", + vma->vm_start, vma->vm_end, vma->vm_pgoff); + return -EINVAL; + } + + vma->vm_pgoff += lpc_res_addr >> PAGE_SHIFT; + prot = pgprot_val(vma->vm_page_prot); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + prot |= _PAGE_CACHE_UC; +#else + prot |= cachemode2protval(_PAGE_CACHE_MODE_UC); +#endif + vma->vm_page_prot = __pgprot(prot); + + // map resource0 into user space + rc = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + if (rc) { + dev_err(&pdev->dev, "resource mapping failed. rc %d", rc); + } + + return rc; +} + +static int +scd_lpc_enable(struct pci_dev *pdev) +{ + struct scd_dev_priv *priv = pci_get_drvdata(pdev); + struct bin_attribute *res_attr; + int rc = 0; + char *res_attr_name; + + if (pdev->res_attr[0]) { + dev_err(&pdev->dev, "Resources already attached at %d\n", 0); + return -EINVAL; + } + + // map address specified into kernel + priv->mem = (void __iomem*) ioremap_nocache((unsigned int) lpc_res_addr, + lpc_res_size); + priv->mem_len = lpc_res_size; + // save the irq for later use, application can still override later + // by writing into /sys/devices/.../interrupt_irq + priv->interrupt_irq = lpc_irq; + + res_attr = NULL; + res_attr_name = NULL; + priv->lpc_vendor = pdev->vendor; + priv->lpc_device = pdev->device; + pdev->vendor = SCD_PCI_VENDOR_ID; + pdev->device = SCD_PCI_DEVICE_ID; + + // create the resource0 file for the scd + res_attr = kzalloc(sizeof(*res_attr), GFP_ATOMIC); + if (!res_attr) { + rc = -ENOMEM; + goto cleanup; + } + + #define RESOURCE_NAME "resource0" + res_attr_name = kzalloc(sizeof(RESOURCE_NAME) + 1, GFP_ATOMIC); + if (!res_attr_name) { + rc = -ENOMEM; + goto cleanup; + } + sprintf(res_attr_name, RESOURCE_NAME); + + sysfs_bin_attr_init(res_attr); + res_attr->attr.name = res_attr_name; + res_attr->attr.mode = S_IRUSR | S_IWUSR; + res_attr->size = lpc_res_size; + res_attr->mmap = scd_lpc_mmap_resource; + res_attr->private = &pdev->resource[0]; + rc = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); + if (rc) { + dev_err(&pdev->dev, "sysfs resource0 creation failed %d\n", rc); + goto cleanup; + } + pdev->res_attr[0] = res_attr; + return rc; + +cleanup: + // let the general cleanup handle unrolling records already created + if (res_attr) { + kfree(res_attr); + } + if (res_attr_name) { + kfree(res_attr_name); + } + + return rc; +} + +static void +scd_lpc_disable(struct pci_dev *pdev) +{ + struct scd_dev_priv *priv = pci_get_drvdata(pdev); + + if (priv->mem) { + iounmap(priv->mem); + priv->mem = NULL; + } + + if (pdev->res_attr[0]) { + sysfs_remove_bin_file(&pdev->dev.kobj, pdev->res_attr[0]); + kfree(pdev->res_attr[0]->attr.name); + kfree(pdev->res_attr[0]); + pdev->res_attr[0] = 0; + } + + pdev->vendor = priv->lpc_vendor; + pdev->device = priv->lpc_device; + return; +} + +static int __init scd_init(void) +{ + int err; + mutex_init(&scd_mutex); + spin_lock_init(&scd_ptp_lock); + INIT_LIST_HEAD(&scd_list); + + printk(KERN_INFO "scd module installed\n"); + err = pci_register_driver(&scd_driver); + if(!err) + scd_procfs_create(); + + return err; +} + +static void __exit scd_exit(void) +{ + pci_unregister_driver(&scd_driver); + scd_procfs_remove(); + printk(KERN_INFO "scd module removed\n"); +} + +module_init(scd_init); +module_exit(scd_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hugh Holbrook and James Lingard"); +MODULE_DESCRIPTION("scd driver"); diff --git a/platform/broadcom/sonic-platform-modules-arista/src/scd.h b/platform/broadcom/sonic-platform-modules-arista/src/scd.h new file mode 100644 index 000000000000..b3dbbbf38fc4 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/src/scd.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010-2016 Arista Networks, Inc. + * + * 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, see . + * + */ + +// scd linux kernel driver public definitions + +// Allow an ardma handler set to be registered. +struct scd_ardma_ops { + void (*probe)(struct pci_dev *pdev, void *scdregs, + unsigned long ardma_offset, + void *localbus, + unsigned long interrupt_mask_ardma, + unsigned long interrupt_mask_read_offset, + unsigned long interrupt_mask_set_offset, + unsigned long interrupt_mask_clear_offset); + void (*remove)(struct pci_dev *pdev); + void (*shutdown)(struct pci_dev *pdev); + bool (*interrupt)(struct pci_dev *pdev); +}; + +// Allow scd-em callbacks to be registered +struct scd_em_ops { + void (*probe)(struct pci_dev *pdev); + void (*remove)(struct pci_dev *pdev); +}; + +// Allow scd-ext callbacks to be registered +struct scd_ext_ops { + int (*probe)(struct pci_dev *pdev); + void (*remove)(struct pci_dev *pdev); + int (*init_trigger)(struct pci_dev *pdev); +}; + +int scd_register_ardma_ops(struct scd_ardma_ops *ops); +void scd_unregister_ardma_ops(void); +int scd_register_em_ops(struct scd_em_ops *ops); +void scd_unregister_em_ops(void); +int scd_register_ext_ops(struct scd_ext_ops *ops); +void scd_unregister_ext_ops(void); +struct pci_dev *scd_get_pdev(const char *name); +u32 scd_read_register(struct pci_dev *pdev, u32 offset); +void scd_write_register(struct pci_dev *pdev, u32 offset, u32 val); +size_t scd_resource_len(struct pci_dev *pdev); +u64 scd_ptp_timestamp(void); + +// Copyright (c) 2010-2016 Arista Networks, Inc. All rights reserved. +// Arista Networks, Inc. Confidential and Proprietary. diff --git a/platform/broadcom/sonic-platform-modules-arista/src/sonic-support-driver.c b/platform/broadcom/sonic-platform-modules-arista/src/sonic-support-driver.c new file mode 100644 index 000000000000..0ceae5787a1d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/src/sonic-support-driver.c @@ -0,0 +1,1270 @@ +/* Copyright (c) 2017 Arista Networks, Inc. + * + * 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, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scd.h" +#include "sonic-support-driver.h" +#include "gpio-kversfix.h" + +#define SCD_MODULE_NAME "scd" + +#define NUM_SMBUS_MASTERS 8 +#define NUM_SMBUS_BUSES 8 +#define SMBUS_RETRY_COUNT 3 +#define SMBUS_REQUEST_OFFSET 0x10 +#define SMBUS_CONTROL_STATUS_OFFSET 0x20 +#define SMBUS_RESPONSE_OFFSET 0x30 + +#define NUM_LEDS 200 +#define NUM_GPIO_ADDRS 200 +#define NUM_GPIOS 500 +#define NUM_RESET_ADDRS 10 +#define NUM_RESETS 100 +#define RESET_SET_OFFSET 0x00 +#define RESET_CLEAR_OFFSET 0x10 +#define NAME_LENGTH 50 + +/* String constants for SFP/QSFP gpio names */ +static const char *qsfpGpioSuffixes[] = { + "_interrupt", + "_present", + "_interrupt_changed", + "_present_changed", + "_lp_mode", + "_reset", + "_modsel" +}; + +static const char *sfpGpioSuffixes[] = { + "_rxlos", + "_txfault", + "_present", + "_rxlos_changed", + "_txfault_changed", + "_present_changed", + "_txdisable", + "_rate_select0", + "_rate_select1" +}; + +static const char *psuGpioSuffixes[] = { + "1_present", + "2_present" +}; + +static const char *muxGpioSuffixes[] = { + "_sfp_qsfp" +}; + +#define QSFPTYPE 0 +#define SFPTYPE 1 +#define PSUTYPE 2 +#define MUXTYPE 3 + +static int numQsfpBits = ARRAY_SIZE(qsfpGpioSuffixes); +static int numSfpBits = ARRAY_SIZE(sfpGpioSuffixes); +static int numPsuBits = ARRAY_SIZE(psuGpioSuffixes); +static int numMuxBits = ARRAY_SIZE(muxGpioSuffixes); + +struct sonic_master; + +struct sonic_bus { + struct sonic_master *master; + u32 id; + struct i2c_adapter adap; +}; + +struct sonic_master { + u32 id; + u32 req; + u32 cs; + u32 resp; + struct mutex mutex; + struct sonic_bus bus[NUM_SMBUS_BUSES]; +}; + +struct sonic_led { + u32 addr; + struct led_classdev cdev; +}; + + +struct sonic_gpio { + char name[40]; + u32 addr; + u32 mask; + u32 ro; + u32 active_low; + u32 gpio_type; + struct gpio_chip chip; +}; + +struct sonic_reset { + char name[40]; + u32 addr; + u32 mask; + struct gpio_chip chip; +}; + + +struct sonic_master master[NUM_SMBUS_MASTERS]; +u32 master_addrs[NUM_SMBUS_MASTERS + 1]; + +struct sonic_led led[NUM_LEDS]; +u32 led_addrs[NUM_LEDS + 1]; +char led_names[NUM_LEDS + 1][NAME_LENGTH]; +u32 num_led_names; + +struct sonic_gpio gpio[NUM_GPIO_ADDRS]; +u32 gpio_addrs[NUM_GPIO_ADDRS + 1]; +u32 gpio_masks[NUM_GPIO_ADDRS + 1]; +u32 gpio_ro[NUM_GPIO_ADDRS + 1]; +u32 gpio_type[NUM_GPIO_ADDRS + 1]; +u32 gpio_active_low[NUM_GPIO_ADDRS + 1]; +char gpio_names[NUM_GPIOS + 1][NAME_LENGTH]; +u32 num_gpio_names; + +struct sonic_reset reset[NUM_RESET_ADDRS]; +u32 reset_addrs[NUM_RESET_ADDRS + 1]; +u32 reset_masks[NUM_RESET_ADDRS + 1]; +char reset_names[NUM_RESETS + 1][NAME_LENGTH]; +u32 num_reset_names; + +union Request { + u32 reg; + struct { + u32 d:8; + u32 ss:6; + u32 reserved1:2; + u32 dat:2; + u32 t:2; + u32 sp:1; + u32 da:1; + u32 dod:1; + u32 st:1; + u32 bs:4; + u32 ti:4; + } __packed; +}; + +union ControlStatus { + u32 reg; + struct { + u32 reserved1:13; + u32 foe:1; + u32 reserved2:17; + u32 reset:1; + } __packed; +}; + +union Response { + u32 reg; + struct { + u32 d:8; + u32 bus_conflict_error:1; + u32 timeout_error:1; + u32 ack_error:1; + u32 flushed:1; + u32 ti:4; + u32 ss:6; + u32 reserved2:9; + u32 fe:1; + } __packed; +}; + +/* Reference to the pci device */ +static struct pci_dev *pdev_ref; +/* Flag to indicate initialization */ +static int initialized; +/* kobj for the sonic driver */ +static struct kobject *sonic_kobject; + +/* i2c client */ +struct sonic_i2c_client { + int master; + int bus; + int addr; + struct i2c_client *client; + struct list_head next; +}; + +static struct list_head client_list; + +static void master_lock(struct sonic_master *pmaster) +{ + mutex_lock(&pmaster->mutex); +} + +static void master_unlock(struct sonic_master *pmaster) +{ + mutex_unlock(&pmaster->mutex); +} + +static struct mutex sonic_mutex; + +static void sonic_lock(void) +{ + mutex_lock(&sonic_mutex); +} + +static void sonic_unlock(void) +{ + mutex_unlock(&sonic_mutex); +} + +/* SMBus functions */ +static void write_req(struct sonic_master *pmaster, + union Request req) +{ + u32 addr = (u32)pmaster->req; + scd_write_register(pdev_ref, addr, req.reg); +} + +static void write_cs(struct sonic_master *pmaster, + union ControlStatus cs) +{ + scd_write_register(pdev_ref, pmaster->cs, cs.reg); +} + +static union ControlStatus read_cs(struct sonic_master *pmaster) +{ + union ControlStatus cs; + cs.reg = scd_read_register(pdev_ref, pmaster->cs); + return cs; +} + +static union Response read_resp(struct sonic_master *pmaster) +{ + union Response resp; + u32 retry = 10; + + resp.reg = scd_read_register(pdev_ref, pmaster->resp); + while (resp.fe && --retry) { + msleep(10); + resp.reg = scd_read_register(pdev_ref, pmaster->resp); + } + + if (resp.fe) { + sonic_dbg("smbus response: fifo still empty after retries"); + resp.reg = 0xffffffff; + } + + return resp; +} + +static s32 check_resp(struct sonic_master *pmaster, + union Response resp, u32 tid) +{ + const char *error; + int error_ret = -EIO; + + if (resp.ack_error) { + error = "ack"; + goto fail; + } + if (resp.timeout_error) { + error = "timeout"; + goto fail; + } + if (resp.bus_conflict_error) { + error = "conflict"; + goto fail; + } + if (resp.flushed) { + error = "flush"; + goto fail; + } + if (resp.ti != tid) { + error = "tid"; + goto fail; + } + return 0; + + fail: + sonic_dbg("smbus response: %s error. reg = 0x%08x", error, resp.reg); + return error_ret; +} + +static u32 sonic_smbus_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static void smbus_reset(struct sonic_master *pmaster) +{ + union ControlStatus cs; + cs = read_cs(pmaster); + cs.reset = 1; + cs.foe = 1; + write_cs(pmaster, cs); + mdelay(10); + cs.reset = 0; + write_cs(pmaster, cs); +} + +static s32 sonic_smbus_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + int i; + union Request req; + union Response resp; + int ret = 0; + u32 ss = 0; + u32 data_offset = 0; + struct sonic_master *pmaster; + struct sonic_bus *bus; + bus = i2c_get_adapdata(adap); + pmaster = bus->master; + + master_lock(pmaster); + + req.reg = 0; + req.bs = bus->id; + req.t = 1; + + switch (size) { + case I2C_SMBUS_QUICK: + ss = 1; + break; + case I2C_SMBUS_BYTE: + ss = 2; + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_WRITE) { + ss = 3; + } else { + ss = 4; + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_WRITE) { + ss = 4; + } else { + ss = 5; + } + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + data_offset = 1; + if (read_write == I2C_SMBUS_WRITE) { + ss = 2 + data->block[0]; + } else { + ss = 3 + data->block[0]; + } + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_WRITE) { + ss = 3 + data->block[0]; + } else { + master_unlock(pmaster); + ret = sonic_smbus_access(adap, addr, flags, I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, data); + master_lock(pmaster); + if (ret) { + goto fail; + } + ss = 4 + data->block[0]; + } + break; + } + + req.st = 1; + req.ss = ss; + req.d = (((addr & 0xff) << 1) | ((ss <= 2) ? read_write : 0)); + req.dod = 1; + for (i = 0; i < ss; i++) { + if (i == ss - 1) { + req.sp = 1; + req.dat = 3; + } + if (i == 1) { + req.st = 0; + req.ss = 0; + req.d = command; + if (ss == 2) + req.dod = ((read_write == I2C_SMBUS_WRITE) ? 1 : 0); + else + req.dod = 1; + } + if ((i == 2 && read_write == I2C_SMBUS_READ)) { + req.st = 1; + req.d = (((addr & 0xff) << 1) | 1); + } + if (i >= 2 && (read_write == I2C_SMBUS_WRITE)) { + req.d = data->block[data_offset + i - 2]; + } + if ((i == 3 && read_write == I2C_SMBUS_READ)) { + req.dod = 0; + } + req.da = ((!(req.dod || req.sp)) ? 1 : 0); + write_req(pmaster, req); + req.ti++; + req.st = 0; + } + + req.ti = 0; + for (i = 0; i < ss; i++) { + resp = read_resp(pmaster); + ret = check_resp(pmaster, resp, req.ti); + if (ret) { + goto fail; + } + req.ti++; + if (read_write == I2C_SMBUS_READ) { + if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA) { + if (i == ss - 1) { + data->byte = resp.d; + } + } else if (size == I2C_SMBUS_WORD_DATA) { + if (i == ss - 2) { + data->word = resp.d; + } else if (i == ss - 1) { + data->word |= (resp.d << 8); + } + } else { + if (i >= 3) { + if (size == I2C_SMBUS_BLOCK_DATA) { + data->block[i - 3] = resp.d; + } else { + data->block[i - 2] = resp.d; + } + } + } + } + } + + master_unlock(pmaster); + return 0; + + fail: + sonic_dbg("smbus %s failed addr=0x%02x reg=0x%02x size=0x%02x adapter=\"%s\"\n", + (read_write) ? "read" : "write", addr, command, size, adap->name); + smbus_reset(pmaster); + master_unlock(pmaster); + return ret; +} + +static s32 sonic_smbus_access_retry(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + int retry = 0; + int ret; + + do { + ret = sonic_smbus_access(adap, addr, flags, read_write, command, size, data); + if (ret != -EIO) + return ret; + retry++; + sonic_dbg("smbus retrying... %d/%d", retry, SMBUS_RETRY_COUNT); + } while (retry < SMBUS_RETRY_COUNT); + + sonic_warn("smbus %s failed addr=0x%02x reg=0x%02x size=0x%02x " + "adapter=\"%s\"\n", (read_write) ? "read" : "write", + addr, command, size, adap->name); + + return -EIO; +} + +static struct i2c_algorithm sonic_smbus_algorithm = { + .smbus_xfer = sonic_smbus_access_retry, + .functionality = sonic_smbus_func, +}; + +static void smbus_remove(void) +{ + int master_id; + int bus_id; + struct sonic_master *pmaster; + struct sonic_bus *bus; + struct list_head *ptr; + struct sonic_i2c_client *entry; + + /* unregister all i2c clients */ + ptr = client_list.next; + while (ptr != &client_list) { + entry = list_entry(ptr, struct sonic_i2c_client, next); + ptr = ptr->next; + i2c_unregister_device(entry->client); + kfree(entry); + } + + for (master_id = 0; master_id < master_addrs[0]; master_id++) { + pmaster = &master[master_id]; + if (!pmaster) { + continue; + } + for (bus_id = 0; bus_id < ARRAY_SIZE(master->bus); bus_id++) { + bus = &pmaster->bus[bus_id]; + i2c_del_adapter(&bus->adap); + } + } +} + +static s32 smbus_init(void) +{ + int master_id; + int bus_id; + int err; + u32 addr; + struct sonic_master *pmaster; + struct sonic_bus *bus; + + for (master_id = 0; master_id < master_addrs[0]; master_id++) { + addr = master_addrs[master_id + 1]; + pmaster = &master[master_id]; + pmaster->id = master_id; + pmaster->req = addr + SMBUS_REQUEST_OFFSET; + pmaster->cs = addr + SMBUS_CONTROL_STATUS_OFFSET; + pmaster->resp = addr + SMBUS_RESPONSE_OFFSET; + + mutex_init(&pmaster->mutex); + smbus_reset(pmaster); + for (bus_id = 0; bus_id < ARRAY_SIZE(pmaster->bus); bus_id++) { + bus = &pmaster->bus[bus_id]; + bus->master = pmaster; + bus->id = bus_id; + bus->adap.owner = THIS_MODULE; + bus->adap.class = 0; + bus->adap.algo = &sonic_smbus_algorithm; + bus->adap.dev.parent = &(pdev_ref->dev); + scnprintf(bus->adap.name, + sizeof(bus->adap.name), + "SCD SMBus master %d bus %d", pmaster->id, bus->id); + i2c_set_adapdata(&bus->adap, bus); + err = i2c_add_adapter(&bus->adap); + + if (err) { + err = -ENODEV; + goto fail; + } + } + } + return 0; + +fail: + smbus_reset(pmaster); + return err; +} + +static void brightness_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + struct sonic_led *pled = container_of(led_cdev, struct sonic_led, cdev); + u32 reg; + + switch ((int)value) { + case 0: + reg = 0x0006ff00; + break; + case 1: + reg = 0x1006ff00; + break; + case 2: + reg = 0x0806ff00; + break; + case 3: + reg = 0x1806ff00; + break; + case 4: + reg = 0x1406ff00; + break; + case 5: + reg = 0x0C06ff00; + break; + case 6: + reg = 0x1C06ff00; + break; + default: + reg = 0x1806ff00; + break; + } + scd_write_register(pdev_ref, pled->addr, reg); +} + +static void led_remove(void) +{ + int i; + struct sonic_led *pled; + for (i = 0; i < led_addrs[0]; i++) { + pled = &led[i]; + if (pled) { + led_classdev_unregister(&pled->cdev); + } + } +} + + +static s32 led_init(void) +{ + int i; + struct sonic_led *pled; + int ret = 0; + for (i = 0; i < led_addrs[0]; i++) { + pled = &led[i]; + pled->addr = led_addrs[i + 1]; + pled->cdev.name = led_names[i]; + pled->cdev.brightness_set = brightness_set; + ret = led_classdev_register(&(pdev_ref->dev), &pled->cdev); + if (ret) { + goto fail; + } + } + + return 0; + + fail: + led_remove(); + return ret; +} + +static u32 bit_mask(u32 mask, unsigned num) +{ + /* Returns the numth set bit in mask */ + int i; + for (i = 0; i < num; i++) { + mask &= mask - 1; + } + return mask & ~(mask - 1); +} + +static int gpio_get(struct gpio_chip *gc, unsigned gpio_num) +{ + struct sonic_gpio *pgpio = container_of(gc, struct sonic_gpio, chip); + u32 mask = bit_mask(pgpio->mask, gpio_num); + return (scd_read_register(pdev_ref, pgpio->addr) & mask); +} + +static void gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val) +{ + struct sonic_gpio *pgpio = container_of(gc, struct sonic_gpio, chip); + u32 mask = bit_mask(pgpio->mask, gpio_num); + u32 reg = scd_read_register(pdev_ref, pgpio->addr); + reg ^= (((-val) ^ reg) & mask); + scd_write_register(pdev_ref, pgpio->addr, reg); +} + +static int direction_input(struct gpio_chip *gc, unsigned gpio_num) +{ + return 0; +} + +static int direction_output(struct gpio_chip *gc, unsigned gpio_num, int val) +{ + struct sonic_gpio *pgpio = container_of(gc, struct sonic_gpio, chip); + u32 mask = bit_mask(pgpio->mask, gpio_num); + if (pgpio->ro & mask) { + return -EINVAL; + } else { + gpio_set(gc, gpio_num, val); + } + return 0; +} + +static int sonic_gpio_add(struct gpio_chip *chip, u16 hwnum, const char *name, + bool direction_may_change, int active_low) +{ + int ret = 0; + struct gpio_desc *desc; + + sonic_dbg("creating gpio %s hwnum %d\n", name, hwnum); + + desc = gpiochip_request_own_desc(chip, hwnum, name); + if (IS_ERR(desc)) { + sonic_err("failed to request desc for GPIO %s\n", name); + goto fail; + } + + ret = gpiod_export(desc, direction_may_change); + if (ret) { + sonic_err("failed to export GPIO %s\n", name); + goto fail_export; + } + + ret = gpiod_export_link(chip->dev, name, desc); + if (ret) { + sonic_err("failed to export link for GPIO %s\n", name); + goto fail_export_link; + } + + ret = gpiod_sysfs_set_active_low(desc, active_low); + if (ret) { + sonic_err("failed to set active_low setting for GPIO %s\n", name); + goto fail_set_active_mode; + } + + return ret; + + fail_set_active_mode: + sysfs_remove_link(&pdev_ref->dev.kobj, name); + fail_export_link: + gpiod_unexport(desc); + fail_export: + gpiochip_free_own_desc(desc); + fail: + return ret; +} + +static void sonic_gpio_remove(struct gpio_chip *chip, u16 hwnum, const char *name) +{ + u16 gpio = chip->base + hwnum; + struct gpio_desc *desc = gpio_to_desc(gpio); + + sysfs_remove_link(&pdev_ref->dev.kobj, name); + gpiod_unexport(desc); + gpiochip_free_own_desc(desc); +} + +static void gpio_remove(void) +{ + int i; + int j; + int k = 0; + struct sonic_gpio *pgpio; + u32 addr; + char gpio_name_buffer[50]; + + for (i = 0; i < gpio_addrs[0]; i++) { + addr = gpio_addrs[i + 1]; + pgpio = &gpio[i]; + if (!pgpio->addr) { + continue; + } + for (j = 0; j < pgpio->chip.ngpio; j++) { + + strcpy(gpio_name_buffer, gpio_names[k]); + switch (pgpio->gpio_type) { + case QSFPTYPE: + strcat(gpio_name_buffer, qsfpGpioSuffixes[j]); + if (j == numQsfpBits - 1) { + k++; + } + break; + case SFPTYPE: + strcat(gpio_name_buffer, sfpGpioSuffixes[j]); + if (j == numSfpBits - 1) { + k++; + } + break; + case PSUTYPE: + strcat(gpio_name_buffer, psuGpioSuffixes[j]); + if (j == numPsuBits - 1) { + k++; + } + break; + case MUXTYPE: + strcat(gpio_name_buffer, muxGpioSuffixes[j]); + if (j == numMuxBits - 1) { + k++; + } + break; + default: + k++; + break; + } + + sonic_gpio_remove(&pgpio->chip, j, gpio_name_buffer); + } + if (gpiochip_remove(&pgpio->chip) < 0) { + sonic_err("Failed to remove GPIO chip\n"); + } + } +} + +static s32 gpio_init(void) +{ + int i; + int j; + int k = 0; + struct sonic_gpio *pgpio; + int ret = 0; + u32 addr; + u32 mask; + char gpio_name_buffer[50]; + + for (i = 0; i < gpio_addrs[0]; i++) { + addr = gpio_addrs[i + 1]; + pgpio = &gpio[i]; + pgpio->addr = addr; + pgpio->mask = gpio_masks[i + 1]; + pgpio->ro = gpio_ro[0] ? gpio_ro[i + 1] : 0; + pgpio->active_low = gpio_active_low[0] ? gpio_active_low[i + 1] : 0; + snprintf(pgpio->name, sizeof(pgpio->name), "gpio%d", i + 1); + pgpio->gpio_type = gpio_type[0] ? gpio_type[i + 1] : 0; + pgpio->chip.label = pgpio->name; + pgpio->chip.owner = THIS_MODULE; + pgpio->chip.base = -1; + pgpio->chip.ngpio = hweight_long(pgpio->mask); + pgpio->chip.dev = &(pdev_ref->dev); + pgpio->chip.get = gpio_get; + pgpio->chip.set = gpio_set; + pgpio->chip.direction_input = direction_input; + pgpio->chip.direction_output = direction_output; + ret = gpiochip_add(&pgpio->chip); + if (ret) { + sonic_err("Failed to create GPIO chip\n"); + goto fail; + } + for (j = 0; j < pgpio->chip.ngpio; j++) { + mask = bit_mask(pgpio->mask, j); + strcpy(gpio_name_buffer, gpio_names[k]); + + switch (pgpio->gpio_type) { + case QSFPTYPE: + strcat(gpio_name_buffer, qsfpGpioSuffixes[j]); + if (j == numQsfpBits - 1) { + k++; + } + break; + case SFPTYPE: + strcat(gpio_name_buffer, sfpGpioSuffixes[j]); + if (j == numSfpBits - 1) { + k++; + } + break; + case PSUTYPE: + strcat(gpio_name_buffer, psuGpioSuffixes[j]); + if (j == numPsuBits - 1) { + k++; + } + break; + case MUXTYPE: + strcat(gpio_name_buffer, muxGpioSuffixes[j]); + if (j == numMuxBits - 1) { + k++; + } + break; + default: + k++; + break; + } + + ret = sonic_gpio_add(&pgpio->chip, j, gpio_name_buffer, !(pgpio->ro & mask), + !!(pgpio->active_low & mask)); + if (ret) { + goto fail; + } + } + } + + return 0; + + fail: + sonic_err("Failed to initialize GPIOs\n"); + gpio_remove(); + return ret; +} + +static int reset_get(struct gpio_chip *gc, unsigned reset_num) +{ + struct sonic_reset *pReset = container_of(gc, struct sonic_reset, chip); + u32 mask = bit_mask(pReset->mask, reset_num); + return (scd_read_register(pdev_ref, pReset->addr) & mask); +} + +static void reset_set(struct gpio_chip *gc, unsigned reset_num, int val) +{ + struct sonic_reset *pReset = container_of(gc, struct sonic_reset, chip); + u32 mask = bit_mask(pReset->mask, reset_num); + u32 offset = (val ? RESET_SET_OFFSET : RESET_CLEAR_OFFSET); + scd_write_register(pdev_ref, pReset->addr+offset, mask); +} + +static int reset_direction_input(struct gpio_chip *gc, unsigned reset_num) +{ + return 0; +} + +static int reset_direction_output(struct gpio_chip *gc, unsigned reset_num, int val) +{ + reset_set(gc, reset_num, val); + return 0; +} + +static void reset_remove(void) +{ + int i; + int j; + int k = 0; + struct sonic_reset *pReset; + u32 addr; + for (i = 0; i < reset_addrs[0]; i++) { + addr = reset_addrs[i + 1]; + pReset = &reset[i]; + if (!pReset) { + continue; + } + for (j = 0; j < pReset->chip.ngpio; j++, k++) { + sonic_gpio_remove(&pReset->chip, j, reset_names[k]); + } + if (gpiochip_remove(&pReset->chip) < 0) { + sonic_err("Failed to remove GPIO chip for reset\n"); + } + } +} + +static s32 reset_init(void) +{ + int i; + int j; + int k = 0; + struct sonic_reset *pReset; + int ret = 0; + u32 addr; + for (i = 0; i < reset_addrs[0]; i++) { + addr = reset_addrs[i + 1]; + pReset = &reset[i]; + pReset->addr = addr; + pReset->mask = reset_masks[i + 1]; + snprintf(pReset->name, sizeof(pReset->name), "pReset%d", i + 1); + pReset->chip.label = pReset->name; + pReset->chip.owner = THIS_MODULE; + pReset->chip.base = -1; + pReset->chip.ngpio = hweight_long(pReset->mask); + pReset->chip.dev = &(pdev_ref->dev); + pReset->chip.get = reset_get; + pReset->chip.set = reset_set; + pReset->chip.direction_input = reset_direction_input; + pReset->chip.direction_output = reset_direction_output; + ret = gpiochip_add(&pReset->chip); + if (ret) { + goto fail; + } + for (j = 0; j < pReset->chip.ngpio; j++, k++) { + ret = sonic_gpio_add(&pReset->chip, j, reset_names[k], true, 0); + if (ret) { + goto fail; + } + } + } + + return 0; + + fail: + reset_remove(); + return ret; +} + +static int sonic_finish_init(void) +{ + int err = 0; + + sonic_dbg("Initialize SCD SMBus adapters\n"); + err = smbus_init(); + if (err) { + sonic_err("Error initializing SCD SMBus adapter\n"); + goto fail_smbus; + } + + sonic_dbg("Initialize SCD LEDs adapters\n"); + err = led_init(); + if (err) { + sonic_err("Error initializing SCD LEDs\n"); + goto fail_led; + } + + sonic_dbg("Initialize SCD GPIOs\n"); + err = gpio_init(); + if (err) { + sonic_err("Error initializing GPIOs\n"); + goto fail_gpio; + } + + sonic_dbg("Initialize SCD resets\n"); + err = reset_init(); + if (err) { + sonic_err("Error initializing resets\n"); + goto fail_reset; + } + + initialized = 1; + sonic_info("sonic support initialization complete\n"); + return 0; + +fail_reset: + gpio_remove(); +fail_gpio: + led_remove(); +fail_led: + smbus_remove(); +fail_smbus: + return err; +} + +static ssize_t read_u32_array(u32 array[], char *buf) +{ + ssize_t len = 0; + int i; + sonic_lock(); + for (i = 1; i < array[0] + 1; i++) { + len += scnprintf(buf + len, PAGE_SIZE - len, "0x%08x\n", array[i]); + } + sonic_unlock(); + return len; +} +static ssize_t write_u32_array(u32 array[], int arraylen, const char *buf, + struct device *dev) +{ + ssize_t status = 0; + char * parse; + + sonic_lock(); + if (!initialized) { + parse = get_options(buf, arraylen, array); + } else { + dev_warn(dev, "attempt to change parameter after device initialization\n"); + } + sonic_unlock(); + return status; +} +static ssize_t read_str_array(char array[][NAME_LENGTH], u32 arraylen, char *buf) +{ + ssize_t len = 0; + int i; + sonic_lock(); + for (i = 0; i < arraylen; i++) { + len += scnprintf(buf + len, PAGE_SIZE - len, "%s\n", array[i]); + } + sonic_unlock(); + return len; +} +static ssize_t write_str_array(char array[][NAME_LENGTH], u32 *arraylen, + const char *buf, struct device *dev) +{ + ssize_t status = 0; + char *running = (char *) buf; + char *token; + int i = 0; + sonic_lock(); + if (!initialized) { + do { + token = strsep(&running, ",\n"); + if (token && token[0]) { + strncpy(array[i], token, sizeof(array[i])); + i++; + } + } while (token); + *arraylen = i; + } else { + dev_warn(dev, "attempt to change parameter after device initialization\n"); + } + sonic_unlock(); + return status; +} + +#define SCD_U32_ARRAY_ATTR(_name) \ +static ssize_t show_##_name(struct device *dev, struct device_attribute *attr, \ +char *buf) \ +{ \ + return read_u32_array(_name, buf); \ +} \ +static ssize_t store_##_name(struct device *dev, struct device_attribute *attr, \ +const char *buf, size_t count) \ +{ \ + write_u32_array(_name, ARRAY_SIZE(_name), buf, dev); \ + return count; \ +} \ +static DEVICE_ATTR(_name, S_IRUGO|S_IWUSR|S_IWGRP, show_##_name, store_##_name); + +#define SCD_STR_ARRAY_ATTR(_name) \ +static ssize_t show_##_name(struct device *dev, struct device_attribute *attr, \ +char *buf) \ +{ \ + return read_str_array(_name, num_##_name, buf); \ +} \ +static ssize_t store_##_name(struct device *dev, struct device_attribute *attr, \ +const char *buf, size_t count) \ +{ \ + write_str_array(_name, &num_##_name, buf, dev); \ + return count; \ +} \ +static DEVICE_ATTR(_name, S_IRUGO|S_IWUSR|S_IWGRP, show_##_name, store_##_name); + +#define SCD_DEVICE_ATTR(_name) \ +static ssize_t show_##_name(struct device *dev, struct device_attribute *attr, \ +char *buf) \ +{ \ + int count = 0; \ + sonic_lock(); \ + count = sprintf(buf, "%lu\n", _name); \ + sonic_unlock(); \ + return count; \ +} \ +static ssize_t store_##_name(struct device *dev, struct device_attribute *attr, \ +const char *buf, size_t count) \ +{ \ + unsigned long new_value = simple_strtoul(buf, NULL, 10); \ + sonic_lock(); \ + _name = new_value; \ + sonic_unlock(); \ + return count; \ +} \ +static DEVICE_ATTR(_name, S_IRUGO|S_IWUSR|S_IWGRP, show_##_name, store_##_name); + +SCD_U32_ARRAY_ATTR(master_addrs); + +SCD_U32_ARRAY_ATTR(led_addrs); +SCD_STR_ARRAY_ATTR(led_names); + +SCD_U32_ARRAY_ATTR(gpio_addrs); +SCD_U32_ARRAY_ATTR(gpio_masks); +SCD_U32_ARRAY_ATTR(gpio_ro); +SCD_U32_ARRAY_ATTR(gpio_type); +SCD_U32_ARRAY_ATTR(gpio_active_low); +SCD_STR_ARRAY_ATTR(gpio_names); + +SCD_U32_ARRAY_ATTR(reset_addrs); +SCD_U32_ARRAY_ATTR(reset_masks); +SCD_STR_ARRAY_ATTR(reset_names); + +static struct attribute *sonic_attrs[] = { + &dev_attr_master_addrs.attr, + &dev_attr_led_addrs.attr, + &dev_attr_led_names.attr, + &dev_attr_reset_addrs.attr, + &dev_attr_reset_masks.attr, + &dev_attr_gpio_addrs.attr, + &dev_attr_gpio_masks.attr, + &dev_attr_gpio_ro.attr, + &dev_attr_gpio_type.attr, + &dev_attr_gpio_active_low.attr, + &dev_attr_gpio_names.attr, + &dev_attr_reset_names.attr, + NULL, +}; + +static struct attribute_group sonic_attr_group = { + .attrs = sonic_attrs, + .name = "sonic_support_driver", +}; + +static int +sonic_probe(struct pci_dev *pdev) +{ + int error = 0; + + master_addrs[0] = 0; + led_addrs[0] = 0; + num_led_names = 0; + gpio_addrs[0] = 0; + gpio_masks[0] = 0; + gpio_ro[0] = 0; + gpio_type[0] = 0; + gpio_active_low[0] = 0; + num_gpio_names = 0; + reset_addrs[0] = 0; + reset_masks[0] = 0; + num_reset_names = 0; + + pdev_ref = pdev; + + error = sysfs_create_link(&pdev->dev.kobj, sonic_kobject, sonic_attr_group.name); + if (error) { + sonic_err("Failed to create the sonic sysfs entry link in scd driver\n"); + dev_err(&(pdev->dev), "sysfs_create_link() error %d\n", error); + return -ENODEV; + } + initialized = 0; + + return 0; +} + +static void +sonic_remove(struct pci_dev *pdev) +{ + smbus_remove(); + led_remove(); + gpio_remove(); + reset_remove(); + + sysfs_remove_link(&pdev->dev.kobj, sonic_attr_group.name); + + initialized = 0; + pdev_ref = NULL; + sonic_info("Removed sonic Support Driver\n"); +} + +static int +sonic_init_trigger(struct pci_dev *pdev) { + sonic_finish_init(); + initialized = 1; + return 0; +} + +static struct scd_ext_ops sonic_ops = { + .probe = sonic_probe, + .remove = sonic_remove, + .init_trigger = sonic_init_trigger, +}; + +static int __init sonic_init(void) +{ + int err = 0; + + sonic_info("Module sonic support init\n"); + + mutex_init(&sonic_mutex); + INIT_LIST_HEAD(&client_list); + sonic_kobject = kobject_get(kernel_kobj); + + err = sysfs_create_group(sonic_kobject, &sonic_attr_group); + if (err) { + sonic_err("Could not create sonic sysfs entries\n"); + goto fail_sysfs; + } + + err = scd_register_ext_ops(&sonic_ops); + if (err) { + sonic_warn("scd-sonic: scd_register_sonic_ops failed\n"); + goto fail_ops; + } + + return err; + +fail_ops: + sysfs_remove_group(sonic_kobject, &sonic_attr_group); +fail_sysfs: + mutex_destroy(&sonic_mutex); + return err; +} + +static void __exit sonic_exit(void) +{ + scd_unregister_ext_ops(); + sysfs_remove_group(sonic_kobject, &sonic_attr_group); + kobject_put(sonic_kobject); + sonic_info("Module sonic support driver removed\n"); +} + +module_init(sonic_init); +module_exit(sonic_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arista Networks"); +MODULE_DESCRIPTION("Sonic Support Driver"); diff --git a/platform/broadcom/sonic-platform-modules-arista/src/sonic-support-driver.h b/platform/broadcom/sonic-platform-modules-arista/src/sonic-support-driver.h new file mode 100644 index 000000000000..38b7eb2024d5 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/src/sonic-support-driver.h @@ -0,0 +1,19 @@ +// Copyright (c) 2016 Arista Networks, Inc. All rights reserved. +// Arista Networks, Inc. Confidential and Proprietary. + +#ifndef DRIVER_SONICSUPPORTDRIVER_H +#define DRIVER_SONICSUPPORTDRIVER_H + +#include + +#define sonic_err(fmt, ...) \ + pr_info("sonic: " fmt, ##__VA_ARGS__); +#define sonic_warn(fmt, ...) \ + pr_warn("sonic: " fmt, ##__VA_ARGS__); +#define sonic_info(fmt, ...) \ + pr_info("sonic: " fmt, ##__VA_ARGS__); +#define sonic_dbg(fmt, ...) \ + pr_debug("sonic: " fmt, ##__VA_ARGS__); + +#endif // DRIVER_SOINCSUPPORTDRIVER_H + diff --git a/platform/broadcom/sonic-platform-modules-arista/tests/all-platforms.sh b/platform/broadcom/sonic-platform-modules-arista/tests/all-platforms.sh new file mode 100755 index 000000000000..44a58592cf88 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/tests/all-platforms.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# +# This script provides a simple helper to test if the initscripts for all +# platforms work. +# It runs in simulation mode and don't actually initialize anything but still +# checks most of the codepaths +# + +# enable simulation mode +extra_args="-s" +errors=false + +continue_on_failure=${CONTINUE_ON_FAILURE:-false} +python=${PYTHON:-python} + +# TODO: check if library in venv +script="arista" +[ -x /usr/bin/arista ] && script="/usr/bin/arista" +[ -x utils/arista ] && script="utils/arista" + +try_execute() { + echo "Run: $script $extra_args $@" + if ! $python $script $extra_args "$@" &>/dev/null; then + $python $script $extra_args -v "$@" + errors=true + $continue_on_failure || exit 1 + fi +} + +echo "Trying general commands" +for cmd in help syseeprom platforms; do + try_execute $cmd +done +echo + +# per platform commands +echo "Trying per platform commands" +for platform in $($script $extra_args platforms | awk '/ - / { print $2 }'); do + try_execute -p $platform setup + try_execute -p $platform setup --reset --background + try_execute -p $platform reset --toggle + try_execute -p $platform clean + try_execute -p $platform dump + echo +done + +if $errors; then + echo "Error were seen during testing" + exit 1 +fi + +echo "All done!" diff --git a/platform/broadcom/sonic-platform-modules-arista/utils/98-scd-uio.rules b/platform/broadcom/sonic-platform-modules-arista/utils/98-scd-uio.rules new file mode 100644 index 000000000000..18083d94d245 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/utils/98-scd-uio.rules @@ -0,0 +1,6 @@ +# rule to create a uio device name for the scds uio devices, +# name is "/dev/uio+$PCI_ADDRESS+$REG+$INDEX" where +# PCI_ADDRESS is the scd's pci address in DDDD:BB:SS.F format, +# REG is the irq register index on the scd (there can be more than 1 on modular) +# INDEX is the zero-based index of the uio on the scd (the bit) +KERNEL=="uio*", DRIVERS=="scd", SYMLINK+="%s{name}" diff --git a/platform/broadcom/sonic-platform-modules-arista/utils/arista b/platform/broadcom/sonic-platform-modules-arista/utils/arista new file mode 100755 index 000000000000..f4d944f0292d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/utils/arista @@ -0,0 +1,233 @@ +#!/usr/bin/env python + +from __future__ import print_function, with_statement + +import logging +import logging.handlers +import argparse +import tempfile +import time +import sys +import os + +import arista.platforms +import arista.core.utils as utils +from arista.core.platform import getPlatform, getSysEeprom, getPlatforms +from arista.core.component import Priority + +lock_file = '/var/lock/arista.lock' + +def checkRootPermissions(): + if utils.inSimulation(): + return + + if os.geteuid() != 0: + log.error('You must be root to use this feature') + sys.exit(1) + +def getHostname(): + import socket + try: + return socket.gethostname() + except: + return 'localhost' + +def setupLogging(verbose=False, logfile=None, syslog=False): + loglevel = logging.DEBUG if verbose else logging.INFO + dateFmt = '%Y-%m-%d %H:%M:%S' + + log = logging.getLogger() + log.setLevel(logging.DEBUG) + + logOut = logging.StreamHandler(sys.stdout) + logOut.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) + logOut.setLevel(loglevel) + log.addHandler(logOut) + + if logfile: + logFile = logging.FileHandler(logfile) + logFile.setFormatter(logging.Formatter( + '%(asctime)s.%(msecs)03d %(levelname)s: %(message)s', datefmt=dateFmt)) + log.addHandler(logFile) + + if syslog: + logSys = logging.handlers.SysLogHandler() + # format to rfc5424 format + logSys.setFormatter( + logging.Formatter('{} arista: %(message)s'.format(getHostname()))) + logSys.setLevel(logging.WARNING) + log.addHandler(logSys) + try: + # the connection to the syslog socket happens with the first message + log.info('Attaching to syslog') + except: + log.warning('Failed open syslog') + + +def setupSimulation(): + global lock_file, log_file + + utils.simulation = True + assert utils.inSimulation() + + logging.info('Running in simulation mode') + lock_file = tempfile.mktemp(prefix='arista-', suffix='.lock') + log_file = tempfile.mktemp(prefix='arista-', suffix='.log') + +def forkForLateInitialization(platform): + try: + pid = os.fork() + except OSError: + logging.warn('fork failed, setting up background drivers normally') + else: + if pid > 0: + logging.debug('initializing slow drivers in child %d', pid) + platform.waitForIt() + os._exit(0) + +def doSetup(args, platform): + checkRootPermissions() + + if args.debug: + utils.debug = True + + with utils.FileLock(lock_file): + logging.debug('setting up critical drivers') + platform.setup(Priority.DEFAULT) + + # NOTE: This assumes that none of the resetable devices are + # initialized in background. + # This should stay true in the future. + if args.reset: + logging.debug('taking devices out of reset') + platform.resetOut() + + if args.background: + logging.debug('forking and setting up slow drivers in background') + forkForLateInitialization(platform) + else: + logging.debug('setting up slow drivers normally') + + platform.setup(Priority.BACKGROUND) + + if not args.background: + platform.waitForIt() + +def doClean(args, platform): + checkRootPermissions() + + if args.reset: + logging.debug('putting devices in reset') + platform.resetIn() + + logging.debug('cleaning up platform') + with utils.FileLock(lock_file): + platform.clean() + +def doReset(args, platform): + if args.reset_out: + platform.resetOut() + elif args.reset_in: + platform.resetIn() + elif args.reset_toggle: + platform.resetIn() + time.sleep(args.reset_delay) + platform.resetOut() + else: + logging.info('nothing to do') + +def doPlatforms(args): + print('supported platforms:') + for platform in sorted(getPlatforms()): + print(' -', platform) + +def doSysEeprom(args): + for key, value in getSysEeprom().items(): + print('%s: %s' % (key, value)) + +def todo(*args, **kwargs): + raise NotImplementedError + +def parseArgs(): + parser = argparse.ArgumentParser( + description='Arista driver initialisation framework', + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + parser.add_argument('-p', '--platform', type=str, + help='name of the platform to load') + parser.add_argument('-l', '--logfile', type=str, + help='log file to log to') + parser.add_argument('-v', '--verbose', action='store_true', + help='increase verbosity') + parser.add_argument('-s', '--simulation', action='store_true', + help='force simulation mode') + parser.add_argument('--syslog', action='store_true', + help='also send logs to syslog' ) + + subparsers = parser.add_subparsers(dest='action') + sub = subparsers.add_parser('help', help='print a help message') + sub = subparsers.add_parser('platforms', help='show supported platforms') + sub = subparsers.add_parser('syseeprom', help='show system eeprom content') + + sub = subparsers.add_parser('dump', help='dump information on this platform') + sub = subparsers.add_parser('setup', help='setup drivers for this platform') + sub.add_argument('-r', '--reset', action='store_true', + help='put devices out of reset after init') + sub.add_argument('-d', '--debug', action='store_true', + help='enable debug features for the drivers') + sub.add_argument('-b', '--background', action='store_true', + help='initialize slow, non-critical drivers in background') + sub = subparsers.add_parser('clean', help='unload drivers for this platform') + sub.add_argument('-r', '--reset', action='store_true', + help='put devices in reset before cleanup') + sub = subparsers.add_parser('reset', help='put component in or out reset') + sub.add_argument('device', nargs='*', # TODO: use this + help='device(s) to put in or out of reset') + sub.add_argument('-t', '--toggle', action='store_true', dest='reset_toggle', + help='put components in and out of reset') + sub.add_argument('-i', '--in', action='store_true', dest='reset_in', + help='put components in reset') + sub.add_argument('-o', '--out', action='store_true', dest='reset_out', + help='put components out of reset') + sub.add_argument('-d', '--delay', type=int, default=1, dest='reset_delay', + help='time to wait between in and out in seconds') + + args = parser.parse_args() + if args.action == 'help': + parser.print_help() + sys.exit(0) + return args + +def main(): + args = parseArgs() + + setupLogging(args.verbose, args.logfile, args.syslog) + + if args.simulation: + setupSimulation() + + logging.debug(args) + + generic_commands = { + 'platforms': doPlatforms, + 'syseeprom': doSysEeprom, + } + + platform_commands = { + 'dump': lambda _, x: x.dump(), + 'setup': doSetup, + 'clean': doClean, + 'status': todo, + 'reset': doReset, + } + + if args.action in generic_commands: + generic_commands[args.action](args) + elif args.action in platform_commands: + platform_commands[args.action](args, getPlatform(args.platform)) + else: + logging.error("Command %s doesn't exists", args.command) + +if __name__ == '__main__': + main() + diff --git a/platform/broadcom/sonic-platform-modules-arista/utils/arista-gardena-watchdog b/platform/broadcom/sonic-platform-modules-arista/utils/arista-gardena-watchdog new file mode 100644 index 000000000000..3f028156859b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/utils/arista-gardena-watchdog @@ -0,0 +1,144 @@ +#!/usr/bin/env python + +# Copyright (c) 2018 Arista Networks, Inc. All rights reserved. +# Arista Networks, Inc. Confidential and Proprietary. + +from __future__ import print_function +import sys +import mmap, os +import argparse +import subprocess +from struct import pack, unpack + +WATCHDOG_REG = 0x0120 + +class MmapResource( object ): + """Resource implementation for a directly-mapped memory region.""" + def __init__( self, path ): + try: + fd = os.open( path, os.O_RDWR ) + except EnvironmentError: + print( "FAIL can not open scd memory-map resource file" ) + print( "FAIL are you running on the proper platform?" ) + sys.exit( 1 ) + + try: + size = os.fstat( fd ).st_size + except EnvironmentError: + print( "FAIL can not fstat scd memory-map resource file" ) + print( "FAIL are you running on the proper platform?" ) + sys.exit( 1 ) + + try: + self.mmap_ = mmap.mmap( fd, size, mmap.MAP_SHARED, + mmap.PROT_READ | mmap.PROT_WRITE ) + except EnvironmentError: + print( "FAIL can not map scd memory-map file" ) + print( "FAIL are you running on the proper platform?" ) + sys.exit( 1 ) + finally: + try: + # Note that closing the file descriptor has no effect on the memory map + os.close( fd ) + except EnvironmentError: + print( "FAIL failed to close scd memory-map file" ) + sys.exit( 1 ) + + def read32( self, addr ): + return unpack( 'arista: %s\n' % ( level, msg % tuple( *args ) ) ) + except: + pass + +def scdRegTest( scd, offset, val1, count ): + scd.write32( offset, val1 ) + val2 = scd.read32( offset ) + if val1 != val2: + print( "FAIL: scd write 0x%08x but read back 0x%08x in iter %d" % + ( val1, val2, count ) ) + sys.exit( 17 ) + +def scdScrRegTest( scd ): + scrOffset = 0x0130 + for i in range( 0, 3 ): + scdRegTest( scd, scrOffset, 0xdeadbeef, i ) + scdRegTest( scd, scrOffset, 0xa5a5a5a5, i ) + scdRegTest( scd, scrOffset, 0x00000000, i ) + +def arm( scd, time ): + regValue = 0 + + if time > 0: + # Set enable bit + regValue |= 1 << 31 + + # Powercycle + regValue |= 2 << 29 + + # Timeout value + regValue |= time + + print( 'Reg = {0:32b}'.format( regValue ) ) + scd.write32( WATCHDOG_REG, regValue ) + +def convertToTensMs( timeSec ): + return timeSec * 100 + +def status( scd ): + regValue = scd.read32( WATCHDOG_REG ) + enabled = bool( regValue >> 31 ) + timeout = regValue & ( ( 1 << 16 ) - 1 ) + return { + "enabled": enabled, + "timeout": timeout, + } + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( '-o', '--arm', type=int, + help='Arm the watchdog for X seconds' ) + parser.add_argument( '--stop', action='store_true', + help='Disable the watchdog' ) + parser.add_argument( '--status', action='store_true', + help='Show watchdog status' ) + args = parser.parse_args() + + if not args.arm and not args.stop and not args.status: + print( 'No option specified' ) + sys.exit( 1 ) + + busName = "/sys/bus/pci/devices/0000:06:00.0/resource0" + subprocess.call( [ 'modprobe', 'scd' ] ) + scd = MmapResource( busName ) + + scdScrRegTest( scd ) + + if args.status: + s = status( scd ) + kv = ' '.join( '%s=%s' % ( k, v ) for k, v in s.items() ) + print( 'watchdog: %s' % kv ) + sys.exit( 0 ) + + + time = 0 + if args.arm: + klog( 'watchdog: arm for %ds' % args.arm ) + # Tens of milliseconds + time = args.arm * 100 + if time >= 65536: + print( 'Error Time value is too big' ) + sys.exit( 1 ) + else: + klog( 'watchdog: disable' ) + + arm( scd, time ) + +if __name__ == "__main__": + main() diff --git a/platform/broadcom/sonic-platform-modules-arista/utils/boot-eos b/platform/broadcom/sonic-platform-modules-arista/utils/boot-eos new file mode 100755 index 000000000000..2933446d81e2 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/utils/boot-eos @@ -0,0 +1,207 @@ +#!/usr/bin/env python + +from __future__ import print_function + +import argparse +import hashlib +import logging +import os +import re +import shutil +import subprocess +import urllib +import zipfile + + +sonicMount = "/host" + +def verifySWI(swiPath): + zf = zipfile.ZipFile(swiPath, 'r') + reVer = re.compile(r'.*SWI_VERSION=.*(\d+)\.(\d+)\.(\d+).*') + for str in zf.read("version").splitlines(): + m = reVer.match(str) + if m: + if int(m.group(1)) >= 4 and int(m.group(2)) >= 19 and int(m.group(3)) >= 0: + return "" + else: + return "Unsupported version of EOS" + return "Cannot find version of EOS in SWI file" + +unsupportedPlatforms = ["x86_64-arista_7050_qx32", "x86_64-arista_7050_qx32s"] + +def verifyPlatform(): + # Check that this script run on an ext4 flash + platform = subprocess.check_output(["sonic-cfggen", "-v", "platform", "-m", + "/etc/sonic/minigraph.xml"]) + return (platform.rstrip() not in unsupportedPlatforms) + +def checkSpace(swiPath): + # for EOS we need ~(2 * ). swi already on fs, so we need additional + # bytes. + mb = (1 << 20) + swiMB = os.stat(swiPath).st_size / mb / 100 * 100 + 100 + statvfs = os.statvfs(sonicMount) + freeMB = statvfs.f_frsize * statvfs.f_bavail / mb + return swiMB - freeMB + +def getEOSFiles(ignoreFile): + res = [] + eosFile = os.path.join( sonicMount, "EOS.swi" ) + if eosFile != ignoreFile and os.path.exists(eosFile): + res.append(eosFile) + eosFile = os.path.join( sonicMount, ".boot-image.swi" ) + if eosFile != ignoreFile and os.path.exists(eosFile): + res.append(eosFile) + return res + +def deleteFiles(files): + for file in files: + os.remove(file) + +def queryYesNo(question, default="yes"): + """Ask a yes/no question via raw_input() and return their answer. + + "question" is a string that is presented to the user. + "default" is the presumed answer if the user just hits . + It must be "yes" (the default), "no" or None (meaning + an answer is required of the user). + + The "answer" return value is True for "yes" or False for "no". + """ + valid = {"yes": True, "y": True, "ye": True, + "no": False, "n": False} + if default is None: + prompt = " [y/n] " + elif default == "yes": + prompt = " [Y/n] " + elif default == "no": + prompt = " [y/N] " + else: + raise ValueError("invalid default answer: '%s'" % default) + + while True: + print(question + prompt) + choice = raw_input().lower() + if default is not None and choice == '': + return valid[default] + elif choice in valid: + return valid[choice] + else: + print("Please respond with 'yes' or 'no' " + "(or 'y' or 'n').\n") + +def main(): + parser = argparse.ArgumentParser( + description='Arista tool to boot EOS', + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + + parser._action_groups.pop() + required = parser.add_argument_group('required arguments') + optional = parser.add_argument_group('optional arguments') + required.add_argument('--swi', "-s", help="EOS.swi file", required=True) + optional.add_argument("--md5", "-m", help="MD5 of EOS.swi file") + startupConfig = os.path.join(sonicMount, "startup-config") + startupHelp = "EOS startup-config url. Or you can just put your " \ + "startup-config file to %s" % startupConfig + if os.path.exists(startupConfig): + optional.add_argument("--config", "-c", help=startupHelp) + else: + required.add_argument("--config", "-c", help=startupHelp, required=True) + optional.add_argument("--remove", "-r", help="remove old EOS files if there " + "is not enough space for installation of new version of EOS", + action="store_true") + optional.add_argument("--no-reboot", "-n", help="config only (without reboot)", + action="store_true") + + args = parser.parse_args() + + # 1) check that we have root privileges + if os.geteuid() != 0: + exit("You need to have root privileges to run this script. Please try " + "again using 'sudo'. Exiting.") + + # 2) download EOS swi + if args.swi.startswith(('http://', 'https://', 'ftp://')): + swi_path = "/tmp/EOS.swi" + try: + urllib.urlretrieve(args.swi, swi_path) + except IOError as e: + exit("Cannot download SWI '{}'. Error: '{}'. Exiting.".format( + args.swi, e)) + else: + swi_path = args.swi + if not os.path.exists(swi_path): + exit("SWI path %s does not exists. Exiting." % swi_path) + + # 3) check MD5 of EOS swi + if args.md5: + hash_md5 = hashlib.md5() + with open(swi_path, "rb") as f: + for chunk in iter(lambda: f.read(10 * (1 << 20)), b""): + hash_md5.update(chunk) + if hash_md5.hexdigest() != args.md5: + exit("Invalid MD5 %s. Exiting." % hash_md5.hexdigest()) + + # 4) check that platform supports ext4 + if not verifyPlatform(): + exit("Unsupported platform. Exiting.") + + # 5) check that EOS supports installation on ext4 + err = verifySWI(swi_path) + if err != "": + exit("%s. Exiting." % err) + + # 6) check that we have enough free space for EOS installation + need = checkSpace(swi_path) + if need > 0: + filesToDelete = getEOSFiles(swi_path) + if len(filesToDelete): + if args.remove or queryYesNo("There is not enough space. Do you want to " + "delete old EOS files: "): + deleteFiles(filesToDelete) + need = checkSpace(swi_path) + if need > 0: + exit("Do not have enough space. We need additional ~%dMB in %s to install " + "EOS. Exiting." % (need, sonicMount)) + + # 7) download startup-config + if args.config: + try: + urllib.urlretrieve(args.config, startupConfig) + except Exception as e: + exit("Cannot download startup-config '{}'. Error: '{}'. Exiting.".format( + args.config, e)) + + # 8) move EOS swi file to /host/EOS.swi + swiName = os.path.basename(swi_path) + swiPath = os.path.join(sonicMount, swiName) + logging.info('moving %s to %s', swi_path, swiPath) + shutil.move(swi_path, swiPath) + + # 9) create backup copy of boot-config and create new boot-config to start EOS + bootConfig = os.path.join(sonicMount, "boot-config") + bootConfigBk = os.path.join(sonicMount, "boot-config.sonic") + if os.path.exists(bootConfig): + if os.path.exists(bootConfigBk): + logging.warning('creating backup of current boot-config: %s, but old ' + 'version of boot-config was found', + bootConfigBk + ".1") + os.rename(bootConfig, bootConfigBk + ".1") + else: + logging.info('creating backup of sonic boot-config: %s', bootConfigBk) + os.rename(bootConfig, bootConfigBk) + with open(bootConfig, 'w+') as f: + logging.info('creating EOS boot-config') + f.write("SWI=flash:/%s" % swiName) + + subprocess.call(['sync']) + + # 10) reboot to EOS + if not args.no_reboot: + logging.info('rebooting to EOS') + subprocess.call(['reboot']) + +if __name__ == '__main__': + main() + diff --git a/platform/broadcom/sonic-platform-modules-arista/utils/boot0 b/platform/broadcom/sonic-platform-modules-arista/utils/boot0 new file mode 100755 index 000000000000..e9053f74698a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-arista/utils/boot0 @@ -0,0 +1,122 @@ +#!/bin/sh +# Copyright (C) 2016 Arista Networks, Inc. +# +# 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 3 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, see . + +# Aboot stage 0 boot + +set -x + +kernel=boot/vmlinuz-3.16.0-5-amd64 +initrd=boot/initrd.img-3.16.0-5-amd64 +kernel_params=kernel-params + +aboot_machine="arista_unknown" + +target_path=/mnt/flash + +# expect the swi to be a non empty file +[ -s "$swipath" ] || exit 1 + +bootconfigvars="SWI SWI_COPY POST_LEVEL CONSOLESPEED PASSWORD NETDEV NETAUTO NETIP NETMASK NETGW NETDOMAIN NETDNS NETHW memtest" + +parse_environment_config() { + for n in ${bootconfigvars}; do + eval v="\$$n" + if [ "$v" ]; then + echo "$n=$v" + fi + done +} + +extract_image() { + ## Clean old directory for read-write layer + rm -rf "$target_path/rw" + + ## Unzip the image + unzip -oq "$swipath" -x boot0 -d "$target_path" +} + +write_machine_config() { + ## Detect SKU and create a hardware description file + aboot_version=$(grep ^Aboot /etc/cmdline | sed 's/^.*norcal.-//') + aboot_build_date=$(stat -c %y /bin/sysinit | sed 's/ /T/') + cat < ${target_path}/machine.conf +aboot_version=$aboot_version +aboot_vendor=arista +aboot_platform=x86_64-$aboot_machine +aboot_machine=$aboot_machine +aboot_arch=x86_64 +aboot_build_date=$aboot_build_date +EOF +} + +platform_specific() { + local platform="$(grep -Eo 'platform=[^ ]+' /etc/cmdline | cut -f2 -d=)" + local sid="$(grep -Eo 'sid=[^ ]+' /etc/cmdline | cut -f2 -d=)" + # This is temporary as the platform= and sid= parameters don't provide enough + # information to identify the SKU + # An initramfs hook or a later processing done by the initscripts will be + # required to read the system eeprom + if [ "$platform" = "raven" ]; then + aboot_machine=arista_7050_qx32 + echo "modprobe.blacklist=radeon" >>/tmp/append + fi + if [ "$platform" = "crow" ]; then + aboot_machine=arista_7050_qx32s + echo "modprobe.blacklist=radeon" >>/tmp/append + fi + if [ "$sid" = "Upperlake" ]; then + aboot_machine=arista_7060_cx32s + echo "amd_iommu=off" >> /tmp/append + fi +} + +echo "$append" >/tmp/append +parse_environment_config >>/tmp/append +cat /etc/cmdline | sed "/^\(${bootconfigvars// /\|}\|crashkernel\|loglevel\|ignore_loglevel\)\(\$\|=\)/d;/^\$/d" >>/tmp/append + +echo "rw loop=fs.squashfs loopfstype=squashfs apparmor=1 security=apparmor quiet" >>/tmp/append + +# process platform specific operations +platform_specific + +# use extra parameters from kernel-params hook if the file exists +if [ -f "$target_path/$kernel_params" ]; then + cat "$target_path/$kernel_params" >>/tmp/append +fi + +# setting root partition if not overridden by kernel-params +if ! grep -q "root=" /tmp/append; then + rootdev=$(mount | grep '/mnt/flash' | cut -f1 -d' ') + rootfstype=$(mount | grep '/mnt/flash' | cut -f5 -d' ') + echo "root=$rootdev" >>/tmp/append +fi + +# check the hash file in the image, and determine to install or just skip +GIT_REVISION=$(unzip -p "$swipath" .imagehash) +LOCAL_IMAGEHASH=$(cat $target_path/.imagehash 2>/dev/null || true) +if [ "$GIT_REVISION" != "$LOCAL_IMAGEHASH" ]; then + extract_image + write_machine_config +fi + +# chainloading using kexec +initrd_path="$target_path/$initrd" +kernel_path="$target_path/$kernel" +cmdline="$(tr '\n' ' ' &2 + exit 1 +} + +warn() { + echo "WARNING: $@" 1>&2 + EXIT_CODE=1 +} + +cmd_help () { + echo "usage: reset -s -d [-l]" + echo " -s state: set the resetting state to \"on\" or \"off\"" + echo " -d device: name for a specific device, or \"ALL\" for all devices" + echo " -l: list devices that support reset" +} + +error_and_help() { + echo "ERROR: $@" 1>&2 + cmd_help + exit 1 +} + +get_scd_address() { + SCD_ADDRESS=`ls /sys/module/scd/drivers/pci\:scd | grep -E '[0-9]{4}:[0-9]{2}:[0-9]{2}'` + debug "$SCD_ADDRESS" + [ -z "$SCD_ADDRESS" ] && fatal 'Scd address not not exist.' + local scd_num=`ls -l /sys/module/scd/drivers/pci\:scd | wc -l` + debug $scd_num + if [ $scd_num -lt 1 ]; then + fatal "Scd address not found." + elif [ $scd_num -gt 1 ]; then + fatal "More than 1 SCD, not supported." + fi +} + +#obtain device list +get_devices() { + #scd address + PREFIX=${PREFIX}${SCD_ADDRESS} + #update device list + for device_path in `echo ${PREFIX}/*_reset`; do + local device_name=`basename $device_path | rev | cut -f2- -d '_' | rev` + DEVICES+=("$device_name") + done +} + +write_file() { + if [ ! -f $1 ]; then + warn "Sys file not found: $1." + return 1 + fi + echo $2 > $1 + if [ $? != 0 ]; then + warn "Sys file write error: $1." + return 1 + fi + return 0 +} + +READ_BUFFER="Unknown" +read_file() { + READ_BUFFER="Unknown" + local result=`cat $1 2>/dev/null` + if [ $? != 0 ]; then + warn "Sys file read error: $1." + return 1 + else + READ_BUFFER="$result" + return 0 + fi +} + +#enter reset for 1 device +set_reset () { + local device="$1" + local state="$2" + debug "$device $state" + write_file "$PREFIX/${device}_reset/direction" out || return 1 + read_file "$PREFIX/${device}_reset/active_low" || return 1 + local active_low="$READ_BUFFER" + if [[ ( "$state" == "on" && "$active_low" == "0" ) || \ + ( "$state" == "off" && "$active_low" == "1" ) ]]; then + data='1' + else + data='0' + fi + local value_file="$PREFIX/${device}_reset/value" + write_file "$value_file" "$data" || return 1 +} + + +list_all_devices() { + printf '%-15s %-10s %-10s %-10s\n' 'name' 'direction' 'value' 'active_low' + for dev in "${DEVICES[@]}"; do + read_file "$PREFIX/${dev}_reset/direction" + local direction="$READ_BUFFER" + read_file "$PREFIX/${dev}_reset/value" + local value="$READ_BUFFER" + read_file "$PREFIX/${dev}_reset/active_low" + local active_low="$READ_BUFFER" + printf '%-15s %-10s %-10s %-10s\n' "$dev" "$direction" "$value" "$active_low" + done +} + +set_reset_all() { + for dev in "${DEVICES[@]}"; do + set_reset "$dev" "$1" || warn "Failed to set device $dev." + done +} + +########## main start ########### +if [ $EUID -ne 0 ]; then + echo "Please run as root." + exit 1 +fi + +STATE="" +DEVICE="" +LIST=false + +while getopts ":s:d:hl" opt; do + case $opt in + s) + [ "$STATE" ] && error_and_help "Duplicate: $opt." + STATE="$OPTARG" + ;; + d) + [ "$DEVICE" ] && error_and_help "Duplicate: $opt." + DEVICE="$OPTARG" + ;; + l) + LIST=true + break + ;; + h) + cmd_help + exit 0 + ;; + *) + error_and_help "Unknown argument: $opt." + ;; + esac +done + +get_scd_address +get_devices + +if [ $LIST == true ]; then + list_all_devices + exit $EXIT_CODE +fi + +[ -z "$DEVICE" ] && error_and_help 'Device not specified.' +[ -z "$STATE" ] && error_and_help 'State not specified.' + +if [ "$STATE" == "on" ] || [ "$STATE" == "off" ]; then + if [ "$DEVICE" == "ALL" ]; then + set_reset_all "$STATE" + else + set_reset "$DEVICE" "$STATE" || warn "Failed to set device $DEVICE." + fi +else + error_and_help "Illegal argument -s." +fi + +exit $EXIT_CODE +########## main end ########### diff --git a/platform/broadcom/sonic-platform-modules-cel b/platform/broadcom/sonic-platform-modules-cel deleted file mode 160000 index 4c8bcd1584e5..000000000000 --- a/platform/broadcom/sonic-platform-modules-cel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4c8bcd1584e5afb05476a67e0ed82ca02fba6da0 diff --git a/platform/broadcom/sonic-platform-modules-cel/.gitignore b/platform/broadcom/sonic-platform-modules-cel/.gitignore new file mode 100644 index 000000000000..f805e810e5c6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/.gitignore @@ -0,0 +1,33 @@ +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su diff --git a/platform/broadcom/sonic-platform-modules-cel/LICENSE b/platform/broadcom/sonic-platform-modules-cel/LICENSE new file mode 100644 index 000000000000..2386a3920c07 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/LICENSE @@ -0,0 +1,15 @@ +Copyright (C) 2017 Celestica, Inc + +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. diff --git a/platform/broadcom/sonic-platform-modules-cel/README.md b/platform/broadcom/sonic-platform-modules-cel/README.md new file mode 100644 index 000000000000..b09a36b51d95 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/README.md @@ -0,0 +1 @@ +platform drivers for Celestica DX010 for the SONiC project diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/changelog b/platform/broadcom/sonic-platform-modules-cel/debian/changelog new file mode 100644 index 000000000000..3b194229773c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/changelog @@ -0,0 +1,44 @@ +sonic-cel-platform-modules (0.7) unstable; urgency=low + + * Add dx010 plaform gpio sysfs exported when module load. + + -- Pradchaya Phucharoen Wed, 21 Jun 2018 13:29:05 +0700 + +sonic-cel-platform-modules (0.6) unstable; urgency=low + + * Remove unused port-mode switch script. This should be done by hwsku config script. + * Add script to turn off QSFP low power mode when boot up. + + -- Pradchaya Phucharoen Wed, 26 July 2017 10:43:00 +0700 + +sonic-cel-platform-modules (0.5) unstable; urgency=low + + * Add port-mode switch script to support 100G 50G 10G_50G qsfp modes. + * Fix garbage data when using sfputil to read QSFP-transceiver's eeprom. + * Fix incorrect endian in eeprom read word data. + + -- Pradchaya Phucharoen Tue, 18 July 2017 11:30:00 +0700 + +sonic-cel-platform-modules (0.4) unstable; urgency=low + + * Add support for DX010's fancontrol, automatic run-up and FIX bug lpmod + + -- Pariwat Leamsumran Thu, 14 June 2017 16:25:00 +0700 + +sonic-cel-platform-modules (0.3) unstable; urgency=low + + * Add support for DX010's DPS800 + + -- Abhisit Sangjan Thu, 29 May 2017 19:23:00 +0700 + +sonic-cel-platform-modules (0.2) unstable; urgency=low + + * Add support for DX010's LM75B, Watchdog and EMC2305 + + -- Abhisit Sangjan Thu, 25 May 2017 15:26:00 +0700 + +sonic-cel-platform-modules (0.1) unstable; urgency=low + + * Initial release + + -- Abhisit Sangjan Mon, 2 May 2017 14:47:00 +0700 diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/compat b/platform/broadcom/sonic-platform-modules-cel/debian/compat new file mode 100644 index 000000000000..ec635144f600 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/compat @@ -0,0 +1 @@ +9 diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/control b/platform/broadcom/sonic-platform-modules-cel/debian/control new file mode 100644 index 000000000000..72bcd54ee62c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/control @@ -0,0 +1,12 @@ +Source: sonic-cel-platform-modules +Section: main +Priority: extra +Maintainer: Abhisit Sangjan +Build-Depends: debhelper (>= 8.0.0), bzip2 +Standards-Version: 3.9.3 + +Package: platform-modules-dx010 +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as fan, led, sfp + diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.init b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.init new file mode 100644 index 000000000000..34ea6ddc807d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.init @@ -0,0 +1,147 @@ +#!/bin/bash + +### BEGIN INIT INFO +# Provides: setup-board +# Required-Start: $portmap +# Required-Stop: +# Should-Start: +# Should-Stop: +# Default-Start: S +# Default-Stop: 0 6 +# Short-Description: Setup DX010 board. +### END INIT INFO + +function export_gpio { +label=$2 +gpio_num=$1 +gpio_base=`( cat /sys/class/gpio/gpiochip*/base | head -1 ) 2>/dev/null` +gpio_label=`( cat /sys/class/gpio/gpiochip*/label | head -1 ) 2>/dev/null` +if [[ "X$gpio_base" == "X" ]] || +( [[ "X$label" != "X" ]] && [[ "$label" != "$gpio_label" ]] ); then + echo "Platform driver error: No gpiochip found!" + exit 1; +fi +ionum=$((gpio_base+gpio_num)) +echo $ionum > /sys/class/gpio/export +if [ $? -ne 0 ]; then + echo "Platform driver error: Cannot export gpio$ionum!" + exit 1; +fi +} + +case "$1" in +start) + echo -n "Setting up board... " + + depmod -a + modprobe i2c-dev + modprobe i2c-mux-pca954x + modprobe dx010_wdt + modprobe leds-dx010 + + found=0 + for devnum in 0 1; do + devname=`cat /sys/bus/i2c/devices/i2c-${devnum}/name` + # iSMT adapter can be at either dffd0000 or dfff0000 + if [[ $devname == 'SMBus iSMT adapter at '* ]]; then + found=1 + break + fi + done + + [ $found -eq 0 ] && echo "cannot find iSMT" && exit 1 + + i2cset -y ${devnum} 0x70 0x10 0x00 0x01 i + + # Attach PCA9541 Ox70 Master Selector + chmod 755 /sys/bus/i2c/devices/i2c-${devnum}/new_device + echo pca9541 0x70 > /sys/bus/i2c/devices/i2c-${devnum}/new_device + sleep 1 + + # Attach PCA9548 0x71 Channel Extender for Main Board + echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-${devnum}/new_device + sleep 1 + + # Attach PCA9548 0x73 Channel Extender for CPU Board + echo pca9548 0x73 > /sys/bus/i2c/devices/i2c-${devnum}/new_device + sleep 1 + + # Attach PCA9548 0x77 Channel Extender for Fan's EEPROMs + echo pca9548 0x77 > /sys/bus/i2c/devices/i2c-${devnum}/new_device + sleep 1 + + # Attach syseeprom + echo 24lc64t 0x50 > /sys/bus/i2c/devices/i2c-12/new_device + + # Attach temperature sensors + echo dx010_lm75b 0x48 > /sys/bus/i2c/devices/i2c-5/new_device + echo dx010_lm75b 0x49 > /sys/bus/i2c/devices/i2c-6/new_device + echo dx010_lm75b 0x4a > /sys/bus/i2c/devices/i2c-7/new_device + echo dx010_lm75b 0x48 > /sys/bus/i2c/devices/i2c-14/new_device + echo dx010_lm75b 0x4e > /sys/bus/i2c/devices/i2c-15/new_device + + # Attach fans + echo emc2305 0x2e > /sys/bus/i2c/devices/i2c-13/new_device + echo emc2305 0x4d > /sys/bus/i2c/devices/i2c-13/new_device + + # Attach PSUs + echo dps460 0x5a > /sys/bus/i2c/devices/i2c-10/new_device + echo dps460 0x5b > /sys/bus/i2c/devices/i2c-11/new_device + + # Attach PCA9506 GPIO expander for 40 pins + echo pca9505 0x20 > /sys/bus/i2c/devices/i2c-17/new_device + + modprobe dx010_cpld + sleep 2 + + # Export platform gpio sysfs + export_gpio 10 # Fan 1 present + export_gpio 11 # Fan 2 present + export_gpio 12 # Fan 3 present + export_gpio 13 # Fan 4 present + export_gpio 14 # Fan 5 present + + export_gpio 22 # PSU L PWOK + export_gpio 25 # PSU R PWOK + export_gpio 27 # PSU L ABS + export_gpio 28 # PSU R ABS + + export_gpio 29 # Fan 1 LED: Red + export_gpio 30 # Fan 1 LED: Yellow + export_gpio 31 # Fan 2 LED: Red + export_gpio 32 # Fan 2 LED: Yellow + export_gpio 33 # Fan 3 LED: Red + export_gpio 34 # Fan 3 LED: Yellow + export_gpio 35 # Fan 4 LED: Red + export_gpio 36 # Fan 4 LED: Yellow + export_gpio 37 # Fan 5 LED: Red + export_gpio 38 # Fan 5 LED: Yellow + + # Turn off/down lpmod by defult (0 - Normal, 1 - Low Pow) + echo 0x00000000 > /sys/devices/platform/dx010_cpld/qsfp_lpmode + + # Attach 32 instances of EEPROM driver QSFP ports + for ((n=26;n<=58;n++)); + do + echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-$n/new_device + sleep 0.1 + done + + echo "done." + ;; + +stop) + echo "done." + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/platform-modules-dx010.init {start|stop}" + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install new file mode 100644 index 000000000000..329b584dc427 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install @@ -0,0 +1,2 @@ +dx010/scripts/dx010_check_qsfp.sh usr/local/bin +dx010/cfg/dx010-modules.conf etc/modules-load.d diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/rules b/platform/broadcom/sonic-platform-modules-cel/debian/rules new file mode 100644 index 000000000000..fa9bb741d5a6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/rules @@ -0,0 +1,33 @@ +#!/usr/bin/make -f + +export INSTALL_MOD_DIR:=extra + +KVERSION ?= $(shell uname -r) +KERNEL_SRC := /lib/modules/$(KVERSION) +MOD_SRC_DIR:= $(shell pwd) +MODULE_DIRS:= dx010 + +%: + dh $@ + +override_dh_auto_build: + (for mod in $(MODULE_DIRS); do \ + make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + done) + +override_dh_auto_install: + (for mod in $(MODULE_DIRS); do \ + dh_installdirs -pplatform-modules-$${mod} \ + $(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$${mod}/modules/*.ko \ + debian/platform-modules-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + done) + +override_dh_usrlocal: + +override_dh_clean: + dh_clean + (for mod in $(MODULE_DIRS); do \ + make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules clean; \ + done) + diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/cfg/dx010-modules.conf b/platform/broadcom/sonic-platform-modules-cel/dx010/cfg/dx010-modules.conf new file mode 100644 index 000000000000..66f002a5fc94 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/cfg/dx010-modules.conf @@ -0,0 +1,15 @@ +# /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 + diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/Makefile b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/Makefile new file mode 100644 index 000000000000..c4109ec772ea --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/Makefile @@ -0,0 +1 @@ +obj-m := dx010_cpld.o mc24lc64t.o emc2305.o dx010_wdt.o leds-dx010.o lm75.o diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_cpld.c b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_cpld.c new file mode 100644 index 000000000000..ab0692faab78 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_cpld.c @@ -0,0 +1,506 @@ +/* + * dx010_cpld.c - driver for SeaStone's CPLD + * + * Copyright (C) 2017 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "dx010_cpld" + +#define LPMOD0108 0x252 +#define LPMOD0910 0x253 +#define LPMOD1118 0x2d2 +#define LPMOD1921 0x2d3 +#define LPMOD2229 0x3d2 +#define LPMOD3032 0x3d3 + +#define ABS0108 0x254 +#define ABS0910 0x255 +#define ABS1118 0x2d4 +#define ABS1921 0x2d5 +#define ABS2229 0x3d4 +#define ABS3032 0x3d5 + +#define INT0108 0x256 +#define INT0910 0x257 +#define INT1118 0x2d6 +#define INT1921 0x2d7 +#define INT2229 0x3d6 +#define INT3032 0x3d7 + +#define LENGTH_PORT_CPLD 34 +#define PORT_BANK1_START 1 +#define PORT_BANK1_END 10 +#define PORT_BANK2_START 11 +#define PORT_BANK2_END 21 +#define PORT_BANK3_START 22 +#define PORT_BANK3_END 32 +#define PORT_SFPP1 33 +#define PORT_SFPP2 34 + +#define PORT_ID_BANK1 0x210 +#define PORT_ID_BANK2 0x290 +#define PORT_ID_BANK3 0x390 + +#define OPCODE_ID_BANK1 0x211 +#define OPCODE_ID_BANK2 0x291 +#define OPCODE_ID_BANK3 0x391 + +#define DEVADDR_ID_BANK1 0x212 +#define DEVADDR_ID_BANK2 0x292 +#define DEVADDR_ID_BANK3 0x392 + +#define CMDBYT_ID_BANK1 0x213 +#define CMDBYT_ID_BANK2 0x293 +#define CMDBYT_ID_BANK3 0x393 + +#define WRITE_ID_BANK1 0x220 +#define WRITE_ID_BANK2 0x2A0 +#define WRITE_ID_BANK3 0x3A0 + +#define READ_ID_BANK1 0x230 +#define READ_ID_BANK2 0x2B0 +#define READ_ID_BANK3 0x3B0 + +#define SSRR_ID_BANK1 0x216 +#define SSRR_ID_BANK2 0x296 +#define SSRR_ID_BANK3 0x396 + +#define HST_CNTL2_QUICK 0x00 +#define HST_CNTL2_BYTE 0x01 +#define HST_CNTL2_BYTE_DATA 0x02 +#define HST_CNTL2_WORD_DATA 0x03 +#define HST_CNTL2_BLOCK 0x05 + +struct dx010_i2c_data { + int portid; +}; + +struct dx010_cpld_data { + struct i2c_adapter *i2c_adapter[LENGTH_PORT_CPLD]; + struct mutex cpld_lock; +}; + +struct dx010_cpld_data *cpld_data; + +static ssize_t get_lpmode(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned long lpmod = 0; + + mutex_lock(&cpld_data->cpld_lock); + + lpmod = + (inb(LPMOD3032) & 0x07) << (24+5) | + inb(LPMOD2229) << (24-3) | + (inb(LPMOD1921) & 0x07) << (16 + 2) | + inb(LPMOD1118) << (16-6) | + (inb(LPMOD0910) & 0x03 ) << 8 | + inb(LPMOD0108); + + mutex_unlock(&cpld_data->cpld_lock); + + return sprintf(buf,"0x%8.8lx\n", lpmod & 0xffffffff); +} + +static ssize_t set_lpmode(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned long lpmod; + int err; + + mutex_lock(&cpld_data->cpld_lock); + + err = kstrtoul(buf, 16, &lpmod); + if (err) + { + mutex_unlock(&cpld_data->cpld_lock); + return err; + } + + outb( (lpmod >> 0) & 0xFF, LPMOD0108); + outb( (lpmod >> 8) & 0x03, LPMOD0910); + outb( (lpmod >> 10) & 0xFF, LPMOD1118); + outb( (lpmod >> 18) & 0x07, LPMOD1921); + outb( (lpmod >> 21) & 0xFF, LPMOD2229); + outb( (lpmod >> 29) & 0x07, LPMOD3032); + + mutex_unlock(&cpld_data->cpld_lock); + + return count; +} + +static ssize_t get_modprs(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned long present; + + mutex_lock(&cpld_data->cpld_lock); + + present = + (inb(ABS3032) & 0x07) << (24+5) | + inb(ABS2229) << (24-3) | + (inb(ABS1921) & 0x07) << (16 + 2) | + inb(ABS1118) << (16-6) | + (inb(ABS0910) & 0x03) << 8 | + inb(ABS0108); + + mutex_unlock(&cpld_data->cpld_lock); + + return sprintf(buf,"0x%8.8lx\n", present & 0xffffffff); +} + +static ssize_t get_modirq(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned long irq; + + mutex_lock(&cpld_data->cpld_lock); + + irq = + (inb(INT3032) & 0x07) << (24+5) | + inb(INT2229) << (24-3) | + (inb(INT1921) & 0x07) << (16 + 2) | + inb(INT1118) << (16-6) | + (inb(INT0910) & 0x03) << 8 | + inb(INT0108); + + mutex_unlock(&cpld_data->cpld_lock); + + return sprintf(buf,"0x%8.8lx\n", irq & 0xffffffff); +} + +static DEVICE_ATTR(qsfp_lpmode, S_IRUGO | S_IWUSR, get_lpmode, set_lpmode); +static DEVICE_ATTR(qsfp_modprs, S_IRUGO, get_modprs, NULL); +static DEVICE_ATTR(qsfp_modirq, S_IRUGO, get_modirq, NULL); + +static struct attribute *dx010_lpc_attrs[] = { + &dev_attr_qsfp_lpmode.attr, + &dev_attr_qsfp_modprs.attr, + &dev_attr_qsfp_modirq.attr, + NULL, +}; + +static struct attribute_group dx010_lpc_attr_grp = { + .attrs = dx010_lpc_attrs, +}; + +static struct resource cel_dx010_lpc_resources[] = { + { + .flags = IORESOURCE_IO, + }, +}; + +static void cel_dx010_lpc_dev_release( struct device * dev) +{ + return; +} + +static struct platform_device cel_dx010_lpc_dev = { + .name = DRIVER_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(cel_dx010_lpc_resources), + .resource = cel_dx010_lpc_resources, + .dev = { + .release = cel_dx010_lpc_dev_release, + } +}; + + +/** + * Read eeprom of QSFP device. + * @param a i2c adapter. + * @param addr address to read. + * @param new_data QSFP port number struct. + * @param cmd i2c command. + * @return 0 if not error, else the error code. + */ +static int i2c_read_eeprom(struct i2c_adapter *a, u16 addr, + struct dx010_i2c_data *new_data, u8 cmd, union i2c_smbus_data *data){ + + u32 reg; + int ioBase=0; + char byte; + short temp; + short portid, opcode, devaddr, cmdbyte0, ssrr, writedata, readdata; + __u16 word_data; + char read_byte; + int error = -EIO; + + mutex_lock(&cpld_data->cpld_lock); + + if (((new_data->portid >= PORT_BANK1_START) + && (new_data->portid <= PORT_BANK1_END)) + || (new_data->portid == PORT_SFPP1) + || (new_data->portid == PORT_SFPP2)) + { + portid = PORT_ID_BANK1; + opcode = OPCODE_ID_BANK1; + devaddr = DEVADDR_ID_BANK1; + cmdbyte0 = CMDBYT_ID_BANK1; + ssrr = SSRR_ID_BANK1; + writedata = WRITE_ID_BANK1; + readdata = READ_ID_BANK1; + }else if ((new_data->portid >= PORT_BANK2_START) && (new_data->portid <= PORT_BANK2_END)){ + portid = PORT_ID_BANK2; + opcode = OPCODE_ID_BANK2; + devaddr = DEVADDR_ID_BANK2; + cmdbyte0 = CMDBYT_ID_BANK2; + ssrr = SSRR_ID_BANK2; + writedata = WRITE_ID_BANK2; + readdata = READ_ID_BANK2; + }else if ((new_data->portid >= PORT_BANK3_START) && (new_data->portid <= PORT_BANK3_END)){ + portid = PORT_ID_BANK3; + opcode = OPCODE_ID_BANK3; + devaddr = DEVADDR_ID_BANK3; + cmdbyte0 = CMDBYT_ID_BANK3; + ssrr = SSRR_ID_BANK3; + writedata = WRITE_ID_BANK3; + readdata = READ_ID_BANK3; + }else{ + /* Invalid parameter! */ + error = -EINVAL; + goto exit; + } + + while ((inb(ioBase + ssrr) & 0x40)); + if ((inb(ioBase + ssrr) & 0x80) == 0x80) { + error = -EIO; + /* Read error reset the port */ + outb(0x00, ioBase + ssrr); + udelay(3000); + outb(0x01, ioBase + ssrr); + goto exit; + } + + byte = 0x40 +new_data->portid; + reg = cmd; + outb(byte, ioBase + portid); + outb(reg,ioBase + cmdbyte0); + byte = 33; + outb(byte, ioBase + opcode); + addr = addr << 1; + addr |= 0x01; + outb(addr, ioBase + devaddr); + while ((inb(ioBase + ssrr) & 0x40)) + { + udelay(100); + } + + if ((inb(ioBase + ssrr) & 0x80) == 0x80) { + /* Read error reset the port */ + error = -EIO; + outb(0x00, ioBase + ssrr); + udelay(3000); + outb(0x01, ioBase + ssrr); + goto exit; + } + + temp = ioBase + readdata; + word_data = inb(temp); + word_data |= (inb(++temp) << 8); + + mutex_unlock(&cpld_data->cpld_lock); + data->word = word_data; + return 0; + +exit: + mutex_unlock(&cpld_data->cpld_lock); + return error; +} + +static int dx010_i2c_access(struct i2c_adapter *a, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data) +{ + + int error = 0; + + struct dx010_i2c_data *new_data; + + /* Write the command register */ + new_data = i2c_get_adapdata(a); + + /* Map the size to what the chip understands */ + switch (size) { + case I2C_SMBUS_QUICK: + size = HST_CNTL2_QUICK; + break; + case I2C_SMBUS_BYTE: + size = HST_CNTL2_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + size = HST_CNTL2_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + size = HST_CNTL2_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + size = HST_CNTL2_BLOCK; + break; + default: + dev_warn(&a->dev, "Unsupported transaction %d\n", size); + error = -EOPNOTSUPP; + goto Done; + } + + switch (size) { + case HST_CNTL2_BYTE: /* Result put in SMBHSTDAT0 */ + break; + case HST_CNTL2_BYTE_DATA: + break; + case HST_CNTL2_WORD_DATA: + if( 0 == i2c_read_eeprom(a,addr,new_data,cmd,data)){ + error = 0; + }else{ + error = -EIO; + } + break; + } + +Done: + return error; +} + +static u32 dx010_i2c_func(struct i2c_adapter *a) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static const struct i2c_algorithm dx010_i2c_algorithm = { + .smbus_xfer = dx010_i2c_access, + .functionality = dx010_i2c_func, +}; + +static struct i2c_adapter * cel_dx010_i2c_init(struct platform_device *pdev, int portid) +{ + int error; + + struct i2c_adapter *new_adapter; + struct dx010_i2c_data *new_data; + + new_adapter = kzalloc(sizeof(*new_adapter), GFP_KERNEL); + if (!new_adapter) + return NULL; + + new_adapter->dev.parent = &pdev->dev; + new_adapter->owner = THIS_MODULE; + new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + new_adapter->algo = &dx010_i2c_algorithm; + + snprintf(new_adapter->name, sizeof(new_adapter->name), + "SMBus dx010 i2c Adapter portid@%04x", portid); + + new_data = kzalloc(sizeof(*new_data), GFP_KERNEL); + if (!new_data) + return NULL; + + new_data->portid = portid; + + i2c_set_adapdata(new_adapter,new_data); + + error = i2c_add_adapter(new_adapter); + if(error) + return NULL; + + return new_adapter; +}; + +static int cel_dx010_lpc_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret =0; + int portid_count; + + cpld_data = devm_kzalloc(&pdev->dev, sizeof(struct dx010_cpld_data), + GFP_KERNEL); + if (!cpld_data) + return -ENOMEM; + + mutex_init(&cpld_data->cpld_lock); + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (unlikely(!res)) { + printk(KERN_ERR " Specified Resource Not Available...\n"); + return -1; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &dx010_lpc_attr_grp); + if (ret) { + printk(KERN_ERR "Cannot create sysfs\n"); + } + + for(portid_count=1 ; portid_count<=LENGTH_PORT_CPLD ; portid_count++) + cpld_data->i2c_adapter[portid_count-1] = + cel_dx010_i2c_init(pdev, portid_count); + + return 0; +} + +static int cel_dx010_lpc_drv_remove(struct platform_device *pdev) +{ + int portid_count; + struct dx010_i2c_data *new_data; + + sysfs_remove_group(&pdev->dev.kobj, &dx010_lpc_attr_grp); + + for (portid_count=1 ; portid_count<=LENGTH_PORT_CPLD ; portid_count++) + i2c_del_adapter(cpld_data->i2c_adapter[portid_count-1]); + + return 0; +} + +static struct platform_driver cel_dx010_lpc_drv = { + .probe = cel_dx010_lpc_drv_probe, + .remove = __exit_p(cel_dx010_lpc_drv_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +int cel_dx010_lpc_init(void) +{ + platform_device_register(&cel_dx010_lpc_dev); + platform_driver_register(&cel_dx010_lpc_drv); + + return 0; +} + +void cel_dx010_lpc_exit(void) +{ + platform_driver_unregister(&cel_dx010_lpc_drv); + platform_device_unregister(&cel_dx010_lpc_dev); +} + +module_init(cel_dx010_lpc_init); +module_exit(cel_dx010_lpc_exit); + +MODULE_AUTHOR("Abhisit Sangjan "); +MODULE_AUTHOR("Pariwat Leamsumran "); +MODULE_DESCRIPTION("Celestica SeaStone DX010 LPC Driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_wdt.c b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_wdt.c new file mode 100644 index 000000000000..a386c065051a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_wdt.c @@ -0,0 +1,215 @@ +/* + * Watchdog driver for the Seastone DX010 + * + * Copyright (C) 2017 Celestica Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DRIVER_NAME "dx010_wdt" + +#define RESET_CTRL 0x102 +#define WDT_MASK 0x04 +#define WDI_GPIO_DIR 0x504 +#define WDI_GPIO 0x508 + +static bool nowayout = WATCHDOG_NOWAYOUT; + +struct dx010_wdt_drvdata { + struct watchdog_device wdt; + struct mutex lock; +}; + +static struct resource dx010_wdt_resources[] = { + { + .flags = IORESOURCE_IO, + }, +}; + +static void dx010_wdt_dev_release( struct device * dev) +{ + return; +} + +static struct platform_device dx010_wdt_dev = { + .name = DRIVER_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dx010_wdt_resources), + .resource = dx010_wdt_resources, + .dev = { + .release = dx010_wdt_dev_release, + } +}; + +static int dx010_wdt_start(struct watchdog_device *wdt_dev) +{ + struct dx010_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); + unsigned char reset_ctrl = 0x00; + unsigned long gpio ,dir; + + mutex_lock(&drvdata->lock); + + gpio = inl(WDI_GPIO); + gpio |= 1 << 15; + outl(gpio, WDI_GPIO); + + outl((inl(WDI_GPIO_DIR) & (~(1 << 15))), WDI_GPIO_DIR); + + reset_ctrl = inb(RESET_CTRL); + + gpio = inl(WDI_GPIO); + gpio &= ~(1 << 15); + outl_p( gpio, WDI_GPIO ); + + mdelay(10); + + gpio = inl(WDI_GPIO); + gpio |= (1 << 15); + outl_p( gpio, WDI_GPIO ); + + reset_ctrl |= WDT_MASK; + outb(reset_ctrl, RESET_CTRL); + + mutex_unlock(&drvdata->lock); + + return 0; +} + +static int dx010_wdt_stop(struct watchdog_device *wdt_dev) +{ + struct dx010_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); + unsigned long reset_ctrl; + + mutex_lock(&drvdata->lock); + + reset_ctrl = inb(RESET_CTRL); + reset_ctrl &= ~WDT_MASK; + outb(reset_ctrl, RESET_CTRL); + + mutex_unlock(&drvdata->lock); + + return 0; +} + +static int dx010_wdt_ping(struct watchdog_device *wdt_dev) +{ + struct dx010_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); + unsigned long gpio; + + mutex_lock(&drvdata->lock); + + gpio = inl(WDI_GPIO); + gpio &= ~(1 << 15); + outl_p( gpio, WDI_GPIO ); + + mdelay(10); + + gpio = inl(WDI_GPIO); + gpio |= (1 << 15); + outl_p( gpio, WDI_GPIO ); + + mutex_unlock(&drvdata->lock); + + return 0; +} + +static const struct watchdog_info dx010_wdt_info = { + .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "DX010 Watchdog", +}; + +static const struct watchdog_ops dx010_wdt_ops = { + .owner = THIS_MODULE, + .start = dx010_wdt_start, + .stop = dx010_wdt_stop, + .ping = dx010_wdt_ping, +}; + +static int dx010_wdt_probe(struct platform_device *pdev) +{ + struct dx010_wdt_drvdata *drvdata; + int ret; + + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), + GFP_KERNEL); + if (!drvdata) { + ret = -ENOMEM; + goto err; + } + + mutex_init(&drvdata->lock); + + drvdata->wdt.info = &dx010_wdt_info; + drvdata->wdt.ops = &dx010_wdt_ops; + + watchdog_set_nowayout(&drvdata->wdt, nowayout); + watchdog_set_drvdata(&drvdata->wdt, drvdata); + + ret = watchdog_register_device(&drvdata->wdt); + if (ret != 0) { + dev_err(&pdev->dev, "watchdog_register_device() failed: %d\n", + ret); + goto err; + } + + platform_set_drvdata(pdev, drvdata); + +err: + return ret; +} + +static int dx010_wdt_remove(struct platform_device *pdev) +{ + struct dx010_wdt_drvdata *drvdata = platform_get_drvdata(pdev); + + watchdog_unregister_device(&drvdata->wdt); + + return 0; +} + +static struct platform_driver dx010_wdt_drv = { + .probe = dx010_wdt_probe, + .remove = dx010_wdt_remove, + .driver = { + .name = DRIVER_NAME, + }, +}; + +int dx010_wdt_init(void) +{ + platform_device_register(&dx010_wdt_dev); + platform_driver_register(&dx010_wdt_drv); + + return 0; +} + +void dx010_wdt_exit(void) +{ + platform_driver_unregister(&dx010_wdt_drv); + platform_device_unregister(&dx010_wdt_dev); +} + +module_init(dx010_wdt_init); +module_exit(dx010_wdt_exit); + +MODULE_AUTHOR("Abhisit Sangjan "); +MODULE_AUTHOR("Pariwat Leamsumran "); +MODULE_DESCRIPTION("Seastone DX010 Watchdog"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:dx010-watchdog"); diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/emc2305.c b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/emc2305.c new file mode 100644 index 000000000000..b406a29fd9be --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/emc2305.c @@ -0,0 +1,886 @@ +/* + * emc2305.c - hwmon driver for SMSC EMC2305 fan controller + * (C) Copyright 2013 + * Reinhard Pfau, Guntermann & Drunck GmbH + * + * Based on emc2103 driver by SMSC. + * + * Datasheet available at: + * http://www.smsc.com/Downloads/SMSC/Downloads_Public/Data_Sheets/2305.pdf + * + * Also supports the EMC2303 fan controller which has the same functionality + * and register layout as EMC2305, but supports only up to 3 fans instead of 5. + * + * Also supports EMC2302 (up to 2 fans) and EMC2301 (1 fan) fan controller. + * + * 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. + */ + +/* + * TODO / IDEAS: + * - expose more of the configuration and features + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Addresses scanned. + * Listed in the same order as they appear in the EMC2305, EMC2303 data sheets. + * + * Note: these are the I2C adresses which are possible for EMC2305 and EMC2303 + * chips. + * The EMC2302 supports only 0x2e (EMC2302-1) and 0x2f (EMC2302-2). + * The EMC2301 supports only 0x2f. + */ +static const unsigned short i2c_adresses[] = { + 0x2E, + 0x2F, + 0x2C, + 0x2D, + 0x4C, + 0x4D, + I2C_CLIENT_END +}; + +/* + * global registers + */ +enum { + REG_CONFIGURATION = 0x20, + REG_FAN_STATUS = 0x24, + REG_FAN_STALL_STATUS = 0x25, + REG_FAN_SPIN_STATUS = 0x26, + REG_DRIVE_FAIL_STATUS = 0x27, + REG_FAN_INTERRUPT_ENABLE = 0x29, + REG_PWM_POLARITY_CONFIG = 0x2a, + REG_PWM_OUTPUT_CONFIG = 0x2b, + REG_PWM_BASE_FREQ_1 = 0x2c, + REG_PWM_BASE_FREQ_2 = 0x2d, + REG_SOFTWARE_LOCK = 0xef, + REG_PRODUCT_FEATURES = 0xfc, + REG_PRODUCT_ID = 0xfd, + REG_MANUFACTURER_ID = 0xfe, + REG_REVISION = 0xff +}; + +/* + * fan specific registers + */ +enum { + REG_FAN_SETTING = 0x30, + REG_PWM_DIVIDE = 0x31, + REG_FAN_CONFIGURATION_1 = 0x32, + REG_FAN_CONFIGURATION_2 = 0x33, + REG_GAIN = 0x35, + REG_FAN_SPIN_UP_CONFIG = 0x36, + REG_FAN_MAX_STEP = 0x37, + REG_FAN_MINIMUM_DRIVE = 0x38, + REG_FAN_VALID_TACH_COUNT = 0x39, + REG_FAN_DRIVE_FAIL_BAND_LOW = 0x3a, + REG_FAN_DRIVE_FAIL_BAND_HIGH = 0x3b, + REG_TACH_TARGET_LOW = 0x3c, + REG_TACH_TARGET_HIGH = 0x3d, + REG_TACH_READ_HIGH = 0x3e, + REG_TACH_READ_LOW = 0x3f, +}; + +#define SEL_FAN(fan, reg) (reg + fan * 0x10) + +/* + * Factor by equations [2] and [3] from data sheet; valid for fans where the + * number of edges equals (poles * 2 + 1). + */ +#define FAN_RPM_FACTOR 3932160 + + +struct emc2305_fan_data { + bool enabled; + bool valid; + unsigned long last_updated; + bool rpm_control; + u8 multiplier; + u8 poles; + u16 target; + u16 tach; + u16 rpm_factor; + u8 pwm; +}; + +struct emc2305_data { + struct device *hwmon_dev; + struct mutex update_lock; + int fans; + struct emc2305_fan_data fan[5]; +}; + +static int read_u8_from_i2c(struct i2c_client *client, u8 i2c_reg, u8 *output) +{ + int status = i2c_smbus_read_byte_data(client, i2c_reg); + if (status < 0) { + dev_warn(&client->dev, "reg 0x%02x, err %d\n", + i2c_reg, status); + } else { + *output = status; + } + return status; +} + +static void read_fan_from_i2c(struct i2c_client *client, u16 *output, + u8 hi_addr, u8 lo_addr) +{ + u8 high_byte, lo_byte; + + if (read_u8_from_i2c(client, hi_addr, &high_byte) < 0) + return; + + if (read_u8_from_i2c(client, lo_addr, &lo_byte) < 0) + return; + + *output = ((u16)high_byte << 5) | (lo_byte >> 3); +} + +static void write_fan_target_to_i2c(struct i2c_client *client, int fan, + u16 new_target) +{ + const u8 lo_reg = SEL_FAN(fan, REG_TACH_TARGET_LOW); + const u8 hi_reg = SEL_FAN(fan, REG_TACH_TARGET_HIGH); + u8 high_byte = (new_target & 0x1fe0) >> 5; + u8 low_byte = (new_target & 0x001f) << 3; + i2c_smbus_write_byte_data(client, lo_reg, low_byte); + i2c_smbus_write_byte_data(client, hi_reg, high_byte); +} + +static void read_fan_config_from_i2c(struct i2c_client *client, int fan) + +{ + struct emc2305_data *data = i2c_get_clientdata(client); + u8 conf1; + + if (read_u8_from_i2c(client, SEL_FAN(fan, REG_FAN_CONFIGURATION_1), + &conf1) < 0) + return; + + data->fan[fan].rpm_control = (conf1 & 0x80) != 0; + data->fan[fan].multiplier = 1 << ((conf1 & 0x60) >> 5); + data->fan[fan].poles = ((conf1 & 0x18) >> 3) + 1; +} + +static void read_fan_setting(struct i2c_client *client, int fan) +{ + struct emc2305_data *data = i2c_get_clientdata(client); + u8 setting; + + if (read_u8_from_i2c(client, SEL_FAN(fan, REG_FAN_SETTING), + &setting) < 0) + return; + + data->fan[fan].pwm = setting; +} + +static void read_fan_data(struct i2c_client *client, int fan_idx) +{ + struct emc2305_data *data = i2c_get_clientdata(client); + + read_fan_from_i2c(client, &data->fan[fan_idx].target, + SEL_FAN(fan_idx, REG_TACH_TARGET_HIGH), + SEL_FAN(fan_idx, REG_TACH_TARGET_LOW)); + read_fan_from_i2c(client, &data->fan[fan_idx].tach, + SEL_FAN(fan_idx, REG_TACH_READ_HIGH), + SEL_FAN(fan_idx, REG_TACH_READ_LOW)); +} + +static struct emc2305_fan_data * +emc2305_update_fan(struct i2c_client *client, int fan_idx) +{ + struct emc2305_data *data = i2c_get_clientdata(client); + struct emc2305_fan_data *fan_data = &data->fan[fan_idx]; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, fan_data->last_updated + HZ + HZ / 2) + || !fan_data->valid) { + read_fan_config_from_i2c(client, fan_idx); + read_fan_data(client, fan_idx); + read_fan_setting(client, fan_idx); + fan_data->valid = true; + fan_data->last_updated = jiffies; + } + + mutex_unlock(&data->update_lock); + return fan_data; +} + +static struct emc2305_fan_data * +emc2305_update_device_fan(struct device *dev, struct device_attribute *da) +{ + struct i2c_client *client = to_i2c_client(dev); + int fan_idx = to_sensor_dev_attr(da)->index; + + return emc2305_update_fan(client, fan_idx); +} + +/* + * set/ config functions + */ + +/* + * Note: we also update the fan target here, because its value is + * determined in part by the fan clock divider. This follows the principle + * of least surprise; the user doesn't expect the fan target to change just + * because the divider changed. + */ +static int +emc2305_set_fan_div(struct i2c_client *client, int fan_idx, long new_div) +{ + struct emc2305_data *data = i2c_get_clientdata(client); + struct emc2305_fan_data *fan = emc2305_update_fan(client, fan_idx); + const u8 reg_conf1 = SEL_FAN(fan_idx, REG_FAN_CONFIGURATION_1); + int new_range_bits, old_div = 8 / fan->multiplier; + int status = 0; + + if (new_div == old_div) /* No change */ + return 0; + + switch (new_div) { + case 1: + new_range_bits = 3; + break; + case 2: + new_range_bits = 2; + break; + case 4: + new_range_bits = 1; + break; + case 8: + new_range_bits = 0; + break; + default: + return -EINVAL; + } + + mutex_lock(&data->update_lock); + + status = i2c_smbus_read_byte_data(client, reg_conf1); + if (status < 0) { + dev_dbg(&client->dev, "reg 0x%02x, err %d\n", + reg_conf1, status); + status = -EIO; + goto exit_unlock; + } + status &= 0x9F; + status |= (new_range_bits << 5); + status = i2c_smbus_write_byte_data(client, reg_conf1, status); + if (status < 0) { + status = -EIO; + goto exit_invalidate; + } + + fan->multiplier = 8 / new_div; + + /* update fan target if high byte is not disabled */ + if ((fan->target & 0x1fe0) != 0x1fe0) { + u16 new_target = (fan->target * old_div) / new_div; + fan->target = min_t(u16, new_target, 0x1fff); + write_fan_target_to_i2c(client, fan_idx, fan->target); + } + +exit_invalidate: + /* invalidate fan data to force re-read from hardware */ + fan->valid = false; +exit_unlock: + mutex_unlock(&data->update_lock); + return status; +} + +static int +emc2305_set_fan_target(struct i2c_client *client, int fan_idx, long rpm_target) +{ + struct emc2305_data *data = i2c_get_clientdata(client); + struct emc2305_fan_data *fan = emc2305_update_fan(client, fan_idx); + + /* + * Datasheet states 16000 as maximum RPM target + * (table 2.2 and section 4.3) + */ + if ((rpm_target < 0) || (rpm_target > 16000)) + return -EINVAL; + + mutex_lock(&data->update_lock); + + if (rpm_target == 0) + fan->target = 0x1fff; + else + fan->target = clamp_val( + (FAN_RPM_FACTOR * fan->multiplier) / rpm_target, + 0, 0x1fff); + + write_fan_target_to_i2c(client, fan_idx, fan->target); + + mutex_unlock(&data->update_lock); + return 0; +} + +static int +emc2305_set_pwm_enable(struct i2c_client *client, int fan_idx, long enable) +{ + struct emc2305_data *data = i2c_get_clientdata(client); + struct emc2305_fan_data *fan = emc2305_update_fan(client, fan_idx); + const u8 reg_fan_conf1 = SEL_FAN(fan_idx, REG_FAN_CONFIGURATION_1); + int status = 0; + u8 conf_reg; + + mutex_lock(&data->update_lock); + switch (enable) { + case 0: + fan->rpm_control = false; + break; + case 3: + fan->rpm_control = true; + break; + default: + status = -EINVAL; + goto exit_unlock; + } + + status = read_u8_from_i2c(client, reg_fan_conf1, &conf_reg); + if (status < 0) { + status = -EIO; + goto exit_unlock; + } + + if (fan->rpm_control) + conf_reg |= 0x80; + else + conf_reg &= ~0x80; + + status = i2c_smbus_write_byte_data(client, reg_fan_conf1, conf_reg); + if (status < 0) + status = -EIO; + +exit_unlock: + mutex_unlock(&data->update_lock); + return status; +} + +static int +emc2305_set_pwm(struct i2c_client *client, int fan_idx, long pwm) +{ + struct emc2305_data *data = i2c_get_clientdata(client); + struct emc2305_fan_data *fan = emc2305_update_fan(client, fan_idx); + const u8 reg_fan_setting = SEL_FAN(fan_idx, REG_FAN_SETTING); + int status = 0; + + /* + * Datasheet states 255 as maximum PWM + * (section 5.7) + */ + if ((pwm < 0) || (pwm > 255)) + return -EINVAL; + + fan->pwm = pwm; + + mutex_lock(&data->update_lock); + + status = i2c_smbus_write_byte_data(client, reg_fan_setting, fan->pwm); + + mutex_unlock(&data->update_lock); + return status; +} +/* + * sysfs callback functions + * + * Note: + * Naming of the funcs is modelled after the naming scheme described in + * Documentation/hwmon/sysfs-interface: + * + * For a sysfs file _ the functions are named like this: + * the show function: show__ + * the store function: set__ + * For read only (RO) attributes of course only the show func is required. + * + * This convention allows us to define the sysfs attributes by using macros. + */ + +static ssize_t +show_fan_input(struct device *dev, struct device_attribute *da, char *buf) +{ + struct emc2305_fan_data *fan = emc2305_update_device_fan(dev, da); + int rpm = 0; + if (fan->tach != 0) + rpm = (FAN_RPM_FACTOR * fan->multiplier) / fan->tach; + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t +show_fan_fault(struct device *dev, struct device_attribute *da, char *buf) +{ + struct emc2305_fan_data *fan = emc2305_update_device_fan(dev, da); + bool fault = ((fan->tach & 0x1fe0) == 0x1fe0); + return sprintf(buf, "%d\n", fault ? 1 : 0); +} + +static ssize_t +show_fan_div(struct device *dev, struct device_attribute *da, char *buf) +{ + struct emc2305_fan_data *fan = emc2305_update_device_fan(dev, da); + int fan_div = 8 / fan->multiplier; + return sprintf(buf, "%d\n", fan_div); +} + +static ssize_t +show_fan_alarm(struct device *dev, struct device_attribute *da, char *buf) +{ + struct emc2305_fan_data *fan = emc2305_update_device_fan(dev, da); + bool fault = ((fan->tach & 0x1fe0) == 0x1fe0); + return sprintf(buf, "%d\n", fault ? 1 : 0); +} + +static ssize_t +set_fan_div(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + int fan_idx = to_sensor_dev_attr(da)->index; + long new_div; + int status; + + status = kstrtol(buf, 10, &new_div); + if (status < 0) + return -EINVAL; + + status = emc2305_set_fan_div(client, fan_idx, new_div); + if (status < 0) + return status; + + return count; +} + +static ssize_t +show_fan_target(struct device *dev, struct device_attribute *da, char *buf) +{ + struct emc2305_fan_data *fan = emc2305_update_device_fan(dev, da); + int rpm = 0; + + /* high byte of 0xff indicates disabled so return 0 */ + if ((fan->target != 0) && ((fan->target & 0x1fe0) != 0x1fe0)) + rpm = (FAN_RPM_FACTOR * fan->multiplier) + / fan->target; + + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t set_fan_target(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + int fan_idx = to_sensor_dev_attr(da)->index; + long rpm_target; + int status; + + status = kstrtol(buf, 10, &rpm_target); + if (status < 0) + return -EINVAL; + + status = emc2305_set_fan_target(client, fan_idx, rpm_target); + if (status < 0) + return status; + + return count; +} + +static ssize_t +show_pwm_enable(struct device *dev, struct device_attribute *da, char *buf) +{ + struct emc2305_fan_data *fan = emc2305_update_device_fan(dev, da); + return sprintf(buf, "%d\n", fan->rpm_control ? 3 : 0); +} + +static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + int fan_idx = to_sensor_dev_attr(da)->index; + long new_value; + int status; + + status = kstrtol(buf, 10, &new_value); + if (status < 0) + return -EINVAL; + status = emc2305_set_pwm_enable(client, fan_idx, new_value); + return count; +} + +static ssize_t show_pwm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct emc2305_fan_data *fan = emc2305_update_device_fan(dev, da); + return sprintf(buf, "%d\n", fan->pwm); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + int fan_idx = to_sensor_dev_attr(da)->index; + unsigned long val; + int ret; + int status; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + if (val > 255) + return -EINVAL; + + status = emc2305_set_pwm(client, fan_idx, val); + return count; +} + +/* define a read only attribute */ +#define EMC2305_ATTR_RO(_type, _item, _num) \ + SENSOR_ATTR(_type ## _num ## _ ## _item, S_IRUGO, \ + show_## _type ## _ ## _item, NULL, _num - 1) + +/* define a read/write attribute */ +#define EMC2305_ATTR_RW(_type, _item, _num) \ + SENSOR_ATTR(_type ## _num ## _ ## _item, S_IRUGO | S_IWUSR, \ + show_## _type ##_ ## _item, \ + set_## _type ## _ ## _item, _num - 1) + +/* + * TODO: Ugly hack, but temporary as this whole logic needs + * to be rewritten as per standard HWMON sysfs registration + */ + +/* define a read/write attribute */ +#define EMC2305_ATTR_RW2(_type, _num) \ + SENSOR_ATTR(_type ## _num, S_IRUGO | S_IWUSR, \ + show_## _type, set_## _type, _num - 1) + +/* defines the attributes for a single fan */ +#define EMC2305_DEFINE_FAN_ATTRS(_num) \ + static const \ + struct sensor_device_attribute emc2305_attr_fan ## _num[] = { \ + EMC2305_ATTR_RO(fan, input, _num), \ + EMC2305_ATTR_RO(fan, fault, _num), \ + EMC2305_ATTR_RW(fan, div, _num), \ + EMC2305_ATTR_RO(fan, alarm, _num), \ + EMC2305_ATTR_RW(fan, target, _num), \ + EMC2305_ATTR_RW(pwm, enable, _num), \ + EMC2305_ATTR_RW2(pwm, _num) \ + } + +#define EMC2305_NUM_FAN_ATTRS ARRAY_SIZE(emc2305_attr_fan1) + +/* common attributes for EMC2303 and EMC2305 */ +static const struct sensor_device_attribute emc2305_attr_common[] = { +}; + +/* fan attributes for the single fans */ +EMC2305_DEFINE_FAN_ATTRS(1); +EMC2305_DEFINE_FAN_ATTRS(2); +EMC2305_DEFINE_FAN_ATTRS(3); +EMC2305_DEFINE_FAN_ATTRS(4); +EMC2305_DEFINE_FAN_ATTRS(5); +EMC2305_DEFINE_FAN_ATTRS(6); + +/* fan attributes */ +static const struct sensor_device_attribute *emc2305_fan_attrs[] = { + emc2305_attr_fan1, + emc2305_attr_fan2, + emc2305_attr_fan3, + emc2305_attr_fan4, + emc2305_attr_fan5, +}; + +/* + * driver interface + */ + +static int emc2305_remove(struct i2c_client *client) +{ + struct emc2305_data *data = i2c_get_clientdata(client); + int fan_idx, i; + + hwmon_device_unregister(data->hwmon_dev); + + for (fan_idx = 0; fan_idx < data->fans; ++fan_idx) + for (i = 0; i < EMC2305_NUM_FAN_ATTRS; ++i) + device_remove_file( + &client->dev, + &emc2305_fan_attrs[fan_idx][i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(emc2305_attr_common); ++i) + device_remove_file(&client->dev, + &emc2305_attr_common[i].dev_attr); + + kfree(data); + return 0; +} + + +#ifdef CONFIG_OF +/* + * device tree support + */ + +struct of_fan_attribute { + const char *name; + int (*set)(struct i2c_client*, int, long); +}; + +struct of_fan_attribute of_fan_attributes[] = { + {"fan-div", emc2305_set_fan_div}, + {"fan-target", emc2305_set_fan_target}, + {"pwm-enable", emc2305_set_pwm_enable}, + {NULL, NULL} +}; + +static int emc2305_config_of(struct i2c_client *client) +{ + struct emc2305_data *data = i2c_get_clientdata(client); + struct device_node *node; + unsigned int fan_idx; + + if (!client->dev.of_node) + return -EINVAL; + if (!of_get_next_child(client->dev.of_node, NULL)) + return 0; + + for (fan_idx = 0; fan_idx < data->fans; ++fan_idx) + data->fan[fan_idx].enabled = false; + + for_each_child_of_node(client->dev.of_node, node) { + const __be32 *property; + int len; + struct of_fan_attribute *attr; + + property = of_get_property(node, "reg", &len); + if (!property || len != sizeof(int)) { + dev_err(&client->dev, "invalid reg on %s\n", + node->full_name); + continue; + } + + fan_idx = be32_to_cpup(property); + if (fan_idx >= data->fans) { + dev_err(&client->dev, + "invalid fan index %d on %s\n", + fan_idx, node->full_name); + continue; + } + + data->fan[fan_idx].enabled = true; + + for (attr = of_fan_attributes; attr->name; ++attr) { + int status = 0; + long value; + property = of_get_property(node, attr->name, &len); + if (!property) + continue; + if (len != sizeof(int)) { + dev_err(&client->dev, "invalid %s on %s\n", + attr->name, node->full_name); + continue; + } + value = be32_to_cpup(property); + status = attr->set(client, fan_idx, value); + if (status == -EINVAL) { + dev_err(&client->dev, + "invalid value for %s on %s\n", + attr->name, node->full_name); + } + } + } + + return 0; +} + +#endif + +static void emc2305_get_config(struct i2c_client *client) +{ + int i; + struct emc2305_data *data = i2c_get_clientdata(client); + + for (i = 0; i < data->fans; ++i) { + data->fan[i].enabled = true; + emc2305_update_fan(client, i); + } + +#ifdef CONFIG_OF + emc2305_config_of(client); +#endif + +} + +static int +emc2305_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct emc2305_data *data; + int status; + int i; + int fan_idx; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + data = kzalloc(sizeof(struct emc2305_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + status = i2c_smbus_read_byte_data(client, REG_PRODUCT_ID); + switch (status) { + case 0x34: /* EMC2305 */ + data->fans = 5; + break; + case 0x35: /* EMC2303 */ + data->fans = 3; + break; + case 0x36: /* EMC2302 */ + data->fans = 2; + break; + case 0x37: /* EMC2301 */ + data->fans = 1; + break; + default: + if (status >= 0) + status = -EINVAL; + goto exit_free; + } + + emc2305_get_config(client); + + for (i = 0; i < ARRAY_SIZE(emc2305_attr_common); ++i) { + status = device_create_file(&client->dev, + &emc2305_attr_common[i].dev_attr); + if (status) + goto exit_remove; + } + for (fan_idx = 0; fan_idx < data->fans; ++fan_idx) + for (i = 0; i < EMC2305_NUM_FAN_ATTRS; ++i) { + if (!data->fan[fan_idx].enabled) + continue; + status = device_create_file( + &client->dev, + &emc2305_fan_attrs[fan_idx][i].dev_attr); + if (status) + goto exit_remove_fans; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove_fans; + } + + dev_info(&client->dev, "%s: sensor '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove_fans: + for (fan_idx = 0; fan_idx < data->fans; ++fan_idx) + for (i = 0; i < EMC2305_NUM_FAN_ATTRS; ++i) + device_remove_file( + &client->dev, + &emc2305_fan_attrs[fan_idx][i].dev_attr); + +exit_remove: + for (i = 0; i < ARRAY_SIZE(emc2305_attr_common); ++i) + device_remove_file(&client->dev, + &emc2305_attr_common[i].dev_attr); +exit_free: + kfree(data); + return status; +} + +static const struct i2c_device_id emc2305_id[] = { + { "emc2305", 0 }, + { "emc2303", 0 }, + { "emc2302", 0 }, + { "emc2301", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, emc2305_id); + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int +emc2305_detect(struct i2c_client *new_client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = new_client->adapter; + int manufacturer, product; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + manufacturer = + i2c_smbus_read_byte_data(new_client, REG_MANUFACTURER_ID); + if (manufacturer != 0x5D) + return -ENODEV; + + product = i2c_smbus_read_byte_data(new_client, REG_PRODUCT_ID); + + switch (product) { + case 0x34: + strlcpy(info->type, "emc2305", I2C_NAME_SIZE); + break; + case 0x35: + strlcpy(info->type, "emc2303", I2C_NAME_SIZE); + break; + case 0x36: + strlcpy(info->type, "emc2302", I2C_NAME_SIZE); + break; + case 0x37: + strlcpy(info->type, "emc2301", I2C_NAME_SIZE); + break; + default: + return -ENODEV; + } + + return 0; +} + +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 = i2c_adresses, +*/ +}; + +module_i2c_driver(emc2305_driver); + +MODULE_AUTHOR("Reinhard Pfau "); +MODULE_DESCRIPTION("SMSC EMC2305 hwmon driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/leds-dx010.c b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/leds-dx010.c new file mode 100644 index 000000000000..fac8322f06b0 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/leds-dx010.c @@ -0,0 +1,289 @@ +/* + * leds-dx010-status.c - Driver for Seastone DX010 front panel LEDs + * + * Copyright (C) 2017 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#define DRIVER_NAME "leds_dx010" +#define FRONT_LED_STAT 0x303 + +static int dx010_led_blink_stat(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + unsigned char led; + + if (!(*delay_on == 0 && *delay_off == 0) && + !(*delay_on == 250 && *delay_off == 250) && + !(*delay_on == 500 && *delay_off == 500)) + return -EINVAL; + + led = inb(FRONT_LED_STAT); + led &= 0xfc; + + if ((*delay_on == 250) && (*delay_off == 250)) + led |= 0x02; + else if ((*delay_on == 500) && (*delay_off == 500)) + led |= 0x01; + + outb(led, FRONT_LED_STAT); + + return 0; +} + +static ssize_t dx010_led_blink_show_stat(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *leddev = dev_get_drvdata(dev); + unsigned char led; + const char *msg; + + led = inb(FRONT_LED_STAT); + led &= 0x03; + + switch (led) + { + case 0: + msg = "No blinking, turn on"; + break; + case 1: + msg = "1 Hz is blinking"; + break; + case 2: + msg = "4 Hz is blinking"; + break; + case 3: + msg = "No blinking, turn off"; + break; + default: + msg = "Unknown error"; + break; + } + + return sprintf(buf, "%s\n", msg); +} + +static ssize_t dx010_led_blink_store_stat(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + struct led_classdev *leddev = dev_get_drvdata(dev); + unsigned long blink_state; + unsigned char led; + + ret = kstrtoul(buf, 10, &blink_state); + if (ret) + return ret; + + led = inb(FRONT_LED_STAT); + led &= 0xfc; + + switch (blink_state) + { + case 0: + led |= 0x03; + break; + case 1: + break; + case 250: + led |= 0x02; + break; + case 500: + led |= 0x01; + break; + default: + return -EINVAL; + break; + } + + outb(led, FRONT_LED_STAT); + + return size; +} +static DEVICE_ATTR(blink, 0644, dx010_led_blink_show_stat, dx010_led_blink_store_stat); + +static void dx010_led_brightness_set_stat(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + unsigned char led; + + led = inb(FRONT_LED_STAT); + led &= 0xfc; + + if (!brightness) + led |= 0x03; + + outb( led, FRONT_LED_STAT); +} + +enum led_brightness dx010_led_brightness_get_p2(struct led_classdev *led_cdev) +{ + unsigned char led; + + led = inb(FRONT_LED_STAT); + + return (led & 0x08) ? LED_OFF : LED_FULL; +} + +static void dx010_led_brightness_set_p2(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + unsigned char led; + + led = inb(FRONT_LED_STAT); + led &= 0xf7; + + if (!brightness) + led |= 0x08; + + outb( led, FRONT_LED_STAT); +} + +enum led_brightness dx010_led_brightness_get_p1(struct led_classdev *led_cdev) +{ + unsigned char led; + + led = inb(FRONT_LED_STAT); + + return (led & 0x04) ? LED_OFF : LED_FULL; +} + +static void dx010_led_brightness_set_p1(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + unsigned char led; + + led = inb(FRONT_LED_STAT); + led &= 0xfb; + + if (!brightness) + led |= 0x04; + + outb( led, FRONT_LED_STAT); +} + +static struct led_classdev dx010_leds[] = { + { + .name = "dx010:green:p-1", + .brightness = LED_OFF, + .max_brightness = 1, + .brightness_get = dx010_led_brightness_get_p1, + .brightness_set = dx010_led_brightness_set_p1, + }, + { + .name = "dx010:green:p-2", + .brightness = LED_OFF, + .max_brightness = 1, + .brightness_get = dx010_led_brightness_get_p2, + .brightness_set = dx010_led_brightness_set_p2, + }, + { + .name = "dx010:green:stat", + .brightness = LED_OFF, + .max_brightness = 1, + .brightness_set = dx010_led_brightness_set_stat, + .blink_set = dx010_led_blink_stat, + .flags = LED_CORE_SUSPENDRESUME, + }, +}; + +static struct resource dx010_led_resources[] = { + { + .flags = IORESOURCE_IO, + }, +}; + +static void dx010_led_dev_release( struct device * dev) +{ + return; +} + +static struct platform_device dx010_lpc_dev = { + .name = DRIVER_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dx010_led_resources), + .resource = dx010_led_resources, + .dev = { + .release = dx010_led_dev_release, + } +}; + +static int dx010_led_drv_probe(struct platform_device *pdev) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(dx010_leds); i++) { + ret = led_classdev_register(&pdev->dev, &dx010_leds[i]); + if (ret < 0) + goto exit; + } + + ret = device_create_file(&pdev->dev, &dev_attr_blink); + if (ret) + { + for (i = 0; i < ARRAY_SIZE(dx010_leds); i++) + led_classdev_unregister(&dx010_leds[i]); + } +exit: + return ret; +} + +static int dx010_led_drv_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dx010_leds); i++) + led_classdev_unregister(&dx010_leds[i]); + + device_remove_file(&pdev->dev, &dev_attr_blink); + + return 0; +} + +static struct platform_driver dx010_led_drv = { + .probe = dx010_led_drv_probe, + .remove = __exit_p(dx010_led_drv_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +int dx010_led_init(void) +{ + platform_device_register(&dx010_lpc_dev); + platform_driver_register(&dx010_led_drv); + + return 0; +} + +void dx010_led_exit(void) +{ + platform_driver_unregister(&dx010_led_drv); + platform_device_unregister(&dx010_lpc_dev); +} + +module_init(dx010_led_init); +module_exit(dx010_led_exit); + +MODULE_AUTHOR("Abhisit Sangjan "); +MODULE_DESCRIPTION("Celestica SeaStone DX010 LEDs Front Panel Status Driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/lm75.c b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/lm75.c new file mode 100644 index 000000000000..a83745212bbf --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/lm75.c @@ -0,0 +1,553 @@ +/* + * lm75.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998, 1999 Frodo Looijaard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lm75.h" + + +/* + * This driver handles the LM75 and compatible digital temperature sensors. + */ + +enum lm75_type { /* keep sorted in alphabetical order */ + adt75, + ds1775, + ds75, + ds7505, + g751, + lm75, + lm75a, + lm75b, + max6625, + max6626, + mcp980x, + stds75, + tcn75, + tmp100, + tmp101, + tmp105, + tmp112, + tmp175, + tmp275, + tmp75, +}; + +/* Addresses scanned */ +static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; + + +/* The LM75 registers */ +#define LM75_REG_CONF 0x01 +static const u8 LM75_REG_TEMP[3] = { + 0x00, /* input */ + 0x03, /* max */ + 0x02, /* hyst */ +}; + +/* Each client has this additional data */ +struct lm75_data { + struct i2c_client *client; + struct device *hwmon_dev; + struct thermal_zone_device *tz; + struct mutex update_lock; + u8 orig_conf; + u8 resolution; /* In bits, between 9 and 12 */ + u8 resolution_limits; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long sample_time; /* In jiffies */ + s16 temp[3]; /* Register values, + 0 = input + 1 = max + 2 = hyst */ +}; + +static int lm75_read_value(struct i2c_client *client, u8 reg); +static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value); +static struct lm75_data *lm75_update_device(struct device *dev); + + +/*-----------------------------------------------------------------------*/ + +static inline long lm75_reg_to_mc(s16 temp, u8 resolution) +{ + return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); +} + +/* sysfs attributes for hwmon */ + +static int lm75_read_temp(void *dev, long *temp) +{ + struct lm75_data *data = lm75_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + *temp = lm75_reg_to_mc(data->temp[0], data->resolution); + + return 0; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct lm75_data *data = lm75_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%ld\n", lm75_reg_to_mc(data->temp[attr->index], + data->resolution)); +} + +static ssize_t set_temp(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct lm75_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int nr = attr->index; + long temp; + int error; + u8 resolution; + + error = kstrtol(buf, 10, &temp); + if (error) + return error; + + /* + * Resolution of limit registers is assumed to be the same as the + * temperature input register resolution unless given explicitly. + */ + if (attr->index && data->resolution_limits) + resolution = data->resolution_limits; + else + resolution = data->resolution; + + mutex_lock(&data->update_lock); + temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); + data->temp[nr] = DIV_ROUND_CLOSEST(temp << (resolution - 8), + 1000) << (16 - resolution); + lm75_write_value(client, LM75_REG_TEMP[nr], data->temp[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, + show_temp, set_temp, 1); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, + show_temp, set_temp, 2); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); + +static struct attribute *lm75_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + + NULL +}; +ATTRIBUTE_GROUPS(lm75); + +/*-----------------------------------------------------------------------*/ + +/* device probe and removal */ + +static int +lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct lm75_data *data; + int status; + u8 set_mask, clr_mask; + int new; + enum lm75_type kind = id->driver_data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -EIO; + + data = devm_kzalloc(dev, sizeof(struct lm75_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Set to LM75 resolution (9 bits, 1/2 degree C) and range. + * Then tweak to be more precise when appropriate. + */ + set_mask = 0; + clr_mask = LM75_SHUTDOWN; /* continuous conversions */ + + switch (kind) { + case adt75: + clr_mask |= 1 << 5; /* not one-shot mode */ + data->resolution = 12; + data->sample_time = HZ / 8; + break; + case ds1775: + case ds75: + case stds75: + clr_mask |= 3 << 5; + set_mask |= 2 << 5; /* 11-bit mode */ + data->resolution = 11; + data->sample_time = HZ; + break; + case ds7505: + set_mask |= 3 << 5; /* 12-bit mode */ + data->resolution = 12; + data->sample_time = HZ / 4; + break; + case g751: + case lm75: + case lm75a: + data->resolution = 9; + data->sample_time = HZ / 2; + break; + case lm75b: + data->resolution = 11; + data->sample_time = HZ / 4; + break; + case max6625: + data->resolution = 9; + data->sample_time = HZ / 4; + break; + case max6626: + data->resolution = 12; + data->resolution_limits = 9; + data->sample_time = HZ / 4; + break; + case tcn75: + data->resolution = 9; + data->sample_time = HZ / 8; + break; + case mcp980x: + data->resolution_limits = 9; + /* fall through */ + case tmp100: + case tmp101: + set_mask |= 3 << 5; /* 12-bit mode */ + data->resolution = 12; + data->sample_time = HZ; + clr_mask |= 1 << 7; /* not one-shot mode */ + break; + case tmp112: + set_mask |= 3 << 5; /* 12-bit mode */ + clr_mask |= 1 << 7; /* not one-shot mode */ + data->resolution = 12; + data->sample_time = HZ / 4; + break; + case tmp105: + case tmp175: + case tmp275: + case tmp75: + set_mask |= 3 << 5; /* 12-bit mode */ + clr_mask |= 1 << 7; /* not one-shot mode */ + data->resolution = 12; + data->sample_time = HZ / 2; + break; + } + + /* configure as specified */ + status = lm75_read_value(client, LM75_REG_CONF); + if (status < 0) { + dev_dbg(dev, "Can't read config? %d\n", status); + return status; + } + data->orig_conf = status; + new = status & ~clr_mask; + new |= set_mask; + if (status != new) + lm75_write_value(client, LM75_REG_CONF, new); + dev_dbg(dev, "Config %02x\n", new); + + data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, + data, lm75_groups); + if (IS_ERR(data->hwmon_dev)) + return PTR_ERR(data->hwmon_dev); + + data->tz = thermal_zone_of_sensor_register(data->hwmon_dev, + 0, + data->hwmon_dev, + lm75_read_temp, NULL); + if (IS_ERR(data->tz)) + data->tz = NULL; + + dev_info(dev, "%s: sensor '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; +} + +static int lm75_remove(struct i2c_client *client) +{ + struct lm75_data *data = i2c_get_clientdata(client); + + thermal_zone_of_sensor_unregister(data->hwmon_dev, data->tz); + hwmon_device_unregister(data->hwmon_dev); + lm75_write_value(client, LM75_REG_CONF, data->orig_conf); + return 0; +} + +static const struct i2c_device_id lm75_ids[] = { + { "adt75", adt75, }, + { "ds1775", ds1775, }, + { "ds75", ds75, }, + { "ds7505", ds7505, }, + { "g751", g751, }, + { "lm75", lm75, }, + { "lm75a", lm75a, }, + { "dx010_lm75b", lm75b, }, + { "max6625", max6625, }, + { "max6626", max6626, }, + { "mcp980x", mcp980x, }, + { "stds75", stds75, }, + { "tcn75", tcn75, }, + { "tmp100", tmp100, }, + { "tmp101", tmp101, }, + { "tmp105", tmp105, }, + { "tmp112", tmp112, }, + { "tmp175", tmp175, }, + { "tmp275", tmp275, }, + { "tmp75", tmp75, }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, lm75_ids); + +#define LM75A_ID 0xA1 + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm75_detect(struct i2c_client *new_client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = new_client->adapter; + int i; + int conf, hyst, os; + bool is_lm75a = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + /* + * Now, we do the remaining detection. There is no identification- + * dedicated register so we have to rely on several tricks: + * unused bits, registers cycling over 8-address boundaries, + * addresses 0x04-0x07 returning the last read value. + * The cycling+unused addresses combination is not tested, + * since it would significantly slow the detection down and would + * hardly add any value. + * + * The National Semiconductor LM75A is different than earlier + * LM75s. It has an ID byte of 0xaX (where X is the chip + * revision, with 1 being the only revision in existence) in + * register 7, and unused registers return 0xff rather than the + * last read value. + * + * Note that this function only detects the original National + * Semiconductor LM75 and the LM75A. Clones from other vendors + * aren't detected, on purpose, because they are typically never + * found on PC hardware. They are found on embedded designs where + * they can be instantiated explicitly so detection is not needed. + * The absence of identification registers on all these clones + * would make their exhaustive detection very difficult and weak, + * and odds are that the driver would bind to unsupported devices. + */ + + /* Unused bits */ + conf = i2c_smbus_read_byte_data(new_client, 1); + if (conf & 0xe0) + return -ENODEV; + + /* First check for LM75A */ + if (i2c_smbus_read_byte_data(new_client, 7) == LM75A_ID) { + /* LM75A returns 0xff on unused registers so + just to be sure we check for that too. */ + if (i2c_smbus_read_byte_data(new_client, 4) != 0xff + || i2c_smbus_read_byte_data(new_client, 5) != 0xff + || i2c_smbus_read_byte_data(new_client, 6) != 0xff) + return -ENODEV; + is_lm75a = 1; + hyst = i2c_smbus_read_byte_data(new_client, 2); + os = i2c_smbus_read_byte_data(new_client, 3); + } else { /* Traditional style LM75 detection */ + /* Unused addresses */ + hyst = i2c_smbus_read_byte_data(new_client, 2); + if (i2c_smbus_read_byte_data(new_client, 4) != hyst + || i2c_smbus_read_byte_data(new_client, 5) != hyst + || i2c_smbus_read_byte_data(new_client, 6) != hyst + || i2c_smbus_read_byte_data(new_client, 7) != hyst) + return -ENODEV; + os = i2c_smbus_read_byte_data(new_client, 3); + if (i2c_smbus_read_byte_data(new_client, 4) != os + || i2c_smbus_read_byte_data(new_client, 5) != os + || i2c_smbus_read_byte_data(new_client, 6) != os + || i2c_smbus_read_byte_data(new_client, 7) != os) + return -ENODEV; + } + + /* Addresses cycling */ + for (i = 8; i <= 248; i += 40) { + if (i2c_smbus_read_byte_data(new_client, i + 1) != conf + || i2c_smbus_read_byte_data(new_client, i + 2) != hyst + || i2c_smbus_read_byte_data(new_client, i + 3) != os) + return -ENODEV; + if (is_lm75a && i2c_smbus_read_byte_data(new_client, i + 7) + != LM75A_ID) + return -ENODEV; + } + + strlcpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE); + + return 0; +} + +#ifdef CONFIG_PM +static int lm75_suspend(struct device *dev) +{ + int status; + struct i2c_client *client = to_i2c_client(dev); + status = lm75_read_value(client, LM75_REG_CONF); + if (status < 0) { + dev_dbg(&client->dev, "Can't read config? %d\n", status); + return status; + } + status = status | LM75_SHUTDOWN; + lm75_write_value(client, LM75_REG_CONF, status); + return 0; +} + +static int lm75_resume(struct device *dev) +{ + int status; + struct i2c_client *client = to_i2c_client(dev); + status = lm75_read_value(client, LM75_REG_CONF); + if (status < 0) { + dev_dbg(&client->dev, "Can't read config? %d\n", status); + return status; + } + status = status & ~LM75_SHUTDOWN; + lm75_write_value(client, LM75_REG_CONF, status); + return 0; +} + +static const struct dev_pm_ops lm75_dev_pm_ops = { + .suspend = lm75_suspend, + .resume = lm75_resume, +}; +#define LM75_DEV_PM_OPS (&lm75_dev_pm_ops) +#else +#define LM75_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +static struct i2c_driver lm75_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "dx010_lm75", + .pm = LM75_DEV_PM_OPS, + }, + .probe = lm75_probe, + .remove = lm75_remove, + .id_table = lm75_ids, +/* + .detect = lm75_detect, + .address_list = normal_i2c, +*/ +}; + +/*-----------------------------------------------------------------------*/ + +/* register access */ + +/* + * All registers are word-sized, except for the configuration register. + * LM75 uses a high-byte first convention, which is exactly opposite to + * the SMBus standard. + */ +static int lm75_read_value(struct i2c_client *client, u8 reg) +{ + if (reg == LM75_REG_CONF) + return i2c_smbus_read_byte_data(client, reg); + else + return i2c_smbus_read_word_swapped(client, reg); +} + +static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if (reg == LM75_REG_CONF) + return i2c_smbus_write_byte_data(client, reg, value); + else + return i2c_smbus_write_word_swapped(client, reg, value); +} + +static struct lm75_data *lm75_update_device(struct device *dev) +{ + struct lm75_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct lm75_data *ret = data; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + data->sample_time) + || !data->valid) { + int i; + dev_dbg(&client->dev, "Starting lm75 update\n"); + + for (i = 0; i < ARRAY_SIZE(data->temp); i++) { + int status; + + status = lm75_read_value(client, LM75_REG_TEMP[i]); + if (unlikely(status < 0)) { + dev_dbg(dev, + "LM75: Failed to read value: reg %d, error %d\n", + LM75_REG_TEMP[i], status); + ret = ERR_PTR(status); + data->valid = 0; + goto abort; + } + data->temp[i] = status; + } + data->last_updated = jiffies; + data->valid = 1; + } + +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +module_i2c_driver(lm75_driver); + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("LM75 driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/lm75.h b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/lm75.h new file mode 100644 index 000000000000..5cde94e56f17 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/lm75.h @@ -0,0 +1,49 @@ +/* + lm75.h - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2003 Mark M. Hoffman + + 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. +*/ + +/* + This file contains common code for encoding/decoding LM75 type + temperature readings, which are emulated by many of the chips + we support. As the user is unlikely to load more than one driver + which contains this code, we don't worry about the wasted space. +*/ + +#include + +/* straight from the datasheet */ +#define LM75_TEMP_MIN (-55000) +#define LM75_TEMP_MAX 125000 +#define LM75_SHUTDOWN 0x01 + +/* TEMP: 0.001C/bit (-55C to +125C) + REG: (0.5C/bit, two's complement) << 7 */ +static inline u16 LM75_TEMP_TO_REG(long temp) +{ + int ntemp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); + ntemp += (ntemp < 0 ? -250 : 250); + return (u16)((ntemp / 500) << 7); +} + +static inline int LM75_TEMP_FROM_REG(u16 reg) +{ + /* use integer division instead of equivalent right shift to + guarantee arithmetic shift and preserve the sign */ + return ((s16)reg / 128) * 500; +} diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/mc24lc64t.c b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/mc24lc64t.c new file mode 100644 index 000000000000..a391056d09a7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/mc24lc64t.c @@ -0,0 +1,142 @@ +/* + * mc24lc64t.c - driver for Microchip 24LC64T + * + * Copyright (C) 2017 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct mc24lc64t_data { + struct i2c_client *fake_client; + struct mutex update_lock; +}; + +static ssize_t mc24lc64t_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = kobj_to_i2c_client(kobj); + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + unsigned long timeout, read_time, i = 0; + int status; + + mutex_lock(&drvdata->update_lock); + + if (i2c_smbus_write_byte_data(client, off>>8, off)) + { + status = -EIO; + goto exit; + } + + msleep(1); + +begin: + + if (i < count) + { + timeout = jiffies + msecs_to_jiffies(25); /* 25 mS timeout*/ + do { + read_time = jiffies; + + status = i2c_smbus_read_byte(client); + if (status >= 0) + { + buf[i++] = status; + goto begin; + } + } while (time_before(read_time, timeout)); + + status = -ETIMEDOUT; + goto exit; + } + + status = count; + +exit: + mutex_unlock(&drvdata->update_lock); + + return status; +} + +static struct bin_attribute mc24lc64t_bit_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO, + }, + .size = 65536, + .read = mc24lc64t_read, +}; + +static int mc24lc64t_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct mc24lc64t_data *drvdata; + int err; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA + | I2C_FUNC_SMBUS_READ_BYTE)) + return -EPFNOSUPPORT; + + if (!(drvdata = devm_kzalloc(&client->dev, + sizeof(struct mc24lc64t_data), GFP_KERNEL))) + return -ENOMEM; + + drvdata->fake_client = i2c_new_dummy(client->adapter, client->addr + 1); + if (!drvdata->fake_client) + return -ENOMEM; + + i2c_set_clientdata(client, drvdata); + mutex_init(&drvdata->update_lock); + + err = sysfs_create_bin_file(&client->dev.kobj, &mc24lc64t_bit_attr); + if (err) + i2c_unregister_device(drvdata->fake_client); + + return err; +} + +static int mc24lc64t_remove(struct i2c_client *client) +{ + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + + i2c_unregister_device(drvdata->fake_client); + + sysfs_remove_bin_file(&client->dev.kobj, &mc24lc64t_bit_attr); + + return 0; +} + +static const struct i2c_device_id mc24lc64t_id[] = { + { "24lc64t", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mc24lc64t_id); + +static struct i2c_driver mc24lc64t_driver = { + .driver = { + .name = "mc24lc64t", + .owner = THIS_MODULE, + }, + .probe = mc24lc64t_probe, + .remove = mc24lc64t_remove, + .id_table = mc24lc64t_id, +}; + +module_i2c_driver(mc24lc64t_driver); + +MODULE_AUTHOR("Abhisit Sangjan "); +MODULE_DESCRIPTION("Microchip 24LC64T Driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/scripts/dx010_check_qsfp.sh b/platform/broadcom/sonic-platform-modules-cel/dx010/scripts/dx010_check_qsfp.sh new file mode 100644 index 000000000000..938b07952e0d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/scripts/dx010_check_qsfp.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +#Usage: +# TBD + +echo "Do we need to check qsfp?" + diff --git a/platform/broadcom/sonic-platform-modules-dell b/platform/broadcom/sonic-platform-modules-dell deleted file mode 160000 index 811e6207330e..000000000000 --- a/platform/broadcom/sonic-platform-modules-dell +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 811e6207330e05ba68d950d4a19ccc573e7b9ee4 diff --git a/platform/broadcom/sonic-platform-modules-dell/.gitignore b/platform/broadcom/sonic-platform-modules-dell/.gitignore new file mode 100644 index 000000000000..f805e810e5c6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/.gitignore @@ -0,0 +1,33 @@ +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su diff --git a/platform/broadcom/sonic-platform-modules-dell/LICENSE b/platform/broadcom/sonic-platform-modules-dell/LICENSE new file mode 100644 index 000000000000..243a846ea80b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/LICENSE @@ -0,0 +1,16 @@ +Copyright (C) 2016 Microsoft, Inc +Copyright (C) 2017 Dell, Inc + +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. diff --git a/platform/broadcom/sonic-platform-modules-dell/README.md b/platform/broadcom/sonic-platform-modules-dell/README.md new file mode 100644 index 000000000000..a9f5358192ad --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/README.md @@ -0,0 +1 @@ +platform drivers for Dell Z9100 for the SONiC project diff --git a/platform/broadcom/sonic-platform-modules-dell/common/dell_i2c_utils.sh b/platform/broadcom/sonic-platform-modules-dell/common/dell_i2c_utils.sh new file mode 100755 index 000000000000..896e0166dc59 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/common/dell_i2c_utils.sh @@ -0,0 +1,62 @@ +# Perform an i2c device configuration : instantiate / delete. +# Input is of the form: +# "echo [driver] > " +# where operation = "new_device" or "delete_device" + +i2c_config() { + local count=0 + local MAX_BUS_RETRY=20 + local MAX_I2C_OP_RETRY=10 + + i2c_bus_op=`echo "$@" | cut -d'>' -f 2` + i2c_bus=$(dirname $i2c_bus_op) + + # check if bus exists + while [[ "$count" -lt "$MAX_BUS_RETRY" ]]; do + [[ -e $i2c_bus ]] && break || sleep .1 + count=$((count+1)) + done + + if [[ "$count" -eq "$MAX_BUS_RETRY" ]]; then + echo "ERROR: $@ : i2c bus not created" + return + fi + + # perform the add/delete + count=0 + while [[ "$count" -lt "$MAX_I2C_OP_RETRY" ]]; do + eval "$@" > /dev/null 2>&1 + [[ $? == 0 ]] && break || sleep .2 + count=$((count+1)) + done + + if [[ "$count" -eq "$MAX_I2C_OP_RETRY" ]]; then + echo "ERROR: $@ : i2c operation failed" + return + fi +} + +# Check if a i2c bus exists. Poll for upto 2 seconds since mux creation may take time.. +# Input: bus to check for existence + +i2c_poll_bus_exists() { + local count=0 + local MAX_BUS_RETRY=20 + local i2c_bus + + i2c_bus=$1 + + # check if bus exists + while [[ "$count" -lt "$MAX_BUS_RETRY" ]]; do + [[ -e $i2c_bus ]] && break || sleep .1 + count=$((count+1)) + done + + if [[ "$count" -eq "$MAX_BUS_RETRY" ]]; then + echo "ERROR: $@ : i2c bus not created" + return 1 + else + return 0 + fi +} + diff --git a/platform/broadcom/sonic-platform-modules-dell/common/dell_pmc.c b/platform/broadcom/sonic-platform-modules-dell/common/dell_pmc.c new file mode 100644 index 000000000000..557976457503 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/common/dell_pmc.c @@ -0,0 +1,1919 @@ +/* Copyright (c) 2017 Dell Inc.* + * dell_s6100_smf.c - driver for Dell SMF + * + * Author: Per Fremrot + * Author: Paavaanan + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIO_DRVNAME "SMF" +#define DEBUG 1 +#define LABELS 1 + +#define FANIN_MAX 12 /* Counted from 1 */ +#define VSEN_MAX 48 /* VSEN1.. */ +#define CURR_MAX 6 +#define TCPU_MAX 15 +#define PSU_MAX 4 /* TODO change to actual sensors */ + +/* Where are the sensors address/data + registers relative to the region offset */ + +#define IOREGION_OFFSET 0x10 +#define IOREGION_LENGTH 0x4 +#define SMF_ADDR_REG_OFFSET 0 +#define SMF_READ_DATA_REG_OFFSET 2 +#define SMF_REG_ADDR 0x200 +#define SMF_PROBE_ADDR 0x210 + +#define SIO_REG_DEVID 0x1 +#define SIO_Z9100_ID 0x1 +#define SIO_S6100_ID 0x2 +#define SIO_S4200_ID 0x3 +#define SIO_S5148_ID 0x4 + +/* IOM presence */ +#define IO_MODULE_STATUS 0x0310 +#define IO_MODULE_PRESENCE 0x0311 + +/* FAN Tray */ +#define S6100_MAX_NUM_FAN_TRAYS 4 +#define Z9100_MAX_NUM_FAN_TRAYS 5 + +#define MAX_NUM_FAN_TRAYS 0x00F0 +#define MAX_NUM_FANS_PER_TRAY 0x00F1 +#define FAN_TRAY_PRESENCE 0x0113 +#define FAN_STATUS_GROUP_A 0x0114 +#define FAN_STATUS_GROUP_B 0x0115 +#define FAN_TRAY_AIRFLOW 0x0116 + + +/* FAN Z9100 */ +#define SMF_FAN_SPEED_ADDR 0x00F3 +#define FAN_TRAY_1_SPEED 0x00F3 +#define FAN_TRAY_1_FAN_2_SPEED 0x00F5 +#define FAN_TRAY_2_SPEED 0x00F7 +#define FAN_TRAY_2_FAN_2_SPEED 0x00F9 +#define FAN_TRAY_3_SPEED 0x00FB +#define FAN_TRAY_3_FAN_2_SPEED 0x00FD +#define FAN_TRAY_4_SPEED 0x00FF +#define FAN_TRAY_4_FAN_2_SPEED 0x0101 +#define FAN_TRAY_5_FAN_1_SPEED 0x0103 +#define FAN_TRAY_5_FAN_2_SPEED 0x0105 +#define FAN_TRAY_5 4 +#define FAN_601_FAULT (2 + 1) +#define IN28_INPUT (27 + 1) +#define IN404_INPUT (43 + 1) +#define IOM_PRESENCE (44 + 1) +#define IOM_PRESENCE_MAX (45 + 1) +#define IN602_INPUT (1 + 1) +#define CURR22_INPUT (1 + 1) +#define CURR602_INPUT (3 + 1) +#define TEMP13_INPUT (12 + 1) +#define TEMP601_INPUT (13 + 1) + +/* PSUs */ +#define S6100_MAX_NUM_PSUS 2 +#define MAX_NUM_PSUS 0x0231 +#define CURRENT_TOTAL_POWER 0x0232 + +/* PSU1 */ +#define PSU_1_MAX_POWER 0x0234 +#define PSU_1_FUNCTION_SUPPORT 0x0236 +#define PSU_1_STATUS 0x0237 +#define PSU_1_TEMPERATURE 0x0239 +#define PSU_1_FAN_SPEED 0x023B +#define PSU_1_FAN_STATUS 0x023D +#define PSU_1_INPUT_VOLTAGE 0x023E +#define PSU_1_OUTPUT_VOLTAGE 0x0240 +#define PSU_1_INPUT_CURRENT 0x0242 +#define PSU_1_OUTPUT_CURRENT 0x0244 +#define PSU_1_INPUT_POWER 0x0246 +#define PSU_1_OUTPUT_POWER 0x0248 +#define PSU_1_FAN_SPEED 0x023B + +/* PSU2 */ +#define PSU_2_MAX_POWER 0x026D +#define PSU_2_FUNCTION_SUPPORT 0x026F +#define PSU_2_STATUS 0x0270 +#define PSU_2_TEMPERATURE 0x0272 +#define PSU_2_FAN_SPEED 0x0274 +#define PSU_2_FAN_STATUS 0x0276 +#define PSU_2_INPUT_VOLTAGE 0x0277 +#define PSU_2_OUTPUT_VOLTAGE 0x0279 +#define PSU_2_INPUT_CURRENT 0x027B +#define PSU_2_OUTPUT_CURRENT 0x027D +#define PSU_2_INPUT_POWER 0x027F +#define PSU_2_OUTPUT_POWER 0x0281 + +/* TEMP */ +#define TEMP_SENSOR_1 0x0014 +#define TEMP_SENSOR_1_STATUS 0x00DC +#define TEMP_SENSOR_1_HW_LIMIT 0x003E + +/* VOLTAGE */ +#define CPU_1_VOLTAGE 0x02A8 +#define IO_MODULE_1_VOLTAGE 0x02E8 +#define SWITCH_CURRENT_S6100 0x02E4 +#define SWITCH_CURRENT_Z9100 0x02E2 + +/* VOLTAGE S6100 */ +#define CPU_1_MONITOR_STATUS 0x0308 +#define CPU_2_MONITOR_STATUS 0x0309 +#define CPU_3_MONITOR_STATUS 0x030A +#define CPU_4_MONITOR_STATUS 0x030B + +/* VOLTAGE Z9100 */ +#define CPU_5_MONITOR_STATUS 0x02E6 +#define CPU_6_MONITOR_STATUS 0x02E7 +#define CPU_7_MONITOR_STATUS 0x02E8 +#define CPU_8_MONITOR_STATUS 0x02E9 + + +unsigned long *mmio; +static struct kobject *dell_kobj; +static unsigned short force_id; +module_param(force_id, ushort, 0); +int smf_ver; + + +enum kinds { + z9100smf, s6100smf +}; + + +struct smf_devices { + const char *name; + u64 tcpu_mask; + u64 vsen_mask; + u32 curr_mask; + u64 fanin_mask; + u64 psu_mask; + const char *const *temp_label; + const char *const *vsen_label; + const char *const *curr_label; + const char *const *fan_label; + const char *const *psu_label; +}; + + +static const char *const z9100_temp_label[] = { + "CPU On-board (U2900)", + "BCM Switch On-Board #1 (U44)", + "Front BCM On-Board (U4)", + "Front BCM On-Board (U2)", + "Unused", + "BCM Switch On-Board #1 (U38)", + "Unused", + "Unused", + "Rear (U2900)", + "", + "", + "", + "", + "", + "PSU 1", + "PSU 2" +}; + + +static const char *const s6100_temp_label[] = { + "CPU On-board (U2900)", + "BCM On-Board #1 (U44)", + "Front BCM On-board (U4)", + "Front BCM On-board (U2)", + "IOM #1", + "IOM #2", + "IOM #3", + "IOM #4", + "U2 Switch board?", + "Front GE", + "Front SFP+", + "BCM Internal", + "CPU Internal", + "", + "PSU 1", + "PSU 2" +}; + + +static const char *const z9100_vsen_label[] = { + /* CPU Board */ + "CPU XP3R3V_EARLY", + "CPU XP5R0V_CP", + "CPU XP3R3V_STD", + "CPU XP3R3V_CP ", + "CPU XP0R75V_VTT_A", + "CPU XP0R75V_VTT_B", + "CPU XP1R07V_CPU", + "CPU XP1R0V_CPU", + "CPU XP12R0V", + "CPU VDDR_CPU_2", + "CPU VDDR_CPU_1", + "CPU XP1R5V_CLK", + "CPU XP1R35V_CPU", + "CPU XP1R8V_CPU", + "CPU XP1R0V_CPU_VNN", + "CPU XP1R0V_CPU_VCC", + "CPU XP1R5V_EARLY", + /* Switch Board */ + "SW XP12R0V_MON", + "SW XP3R3V_MON", + "SW XP1R8V_MON", + "SW XP1R25V_MON", + "SW XP1R2V_MON", + "SW XP1R0V_SW_MON", + "SW XP1R0V_ROV_SW_MON", + "SW XP5V_MB_MON", + "SW XP1R8V_FPGA_MON", + "SW XP3R3V_FPGA_MON", + "SW XP3R3V_EARLY_MON", + /* PSU */ + "PSU1 VIN", + "PSU1 VOUT", + "PSU2 VIN", + "PSU2 VOUT", + /* IOM 1 */ + "", + "", + "", + "", + /* IOM 2 */ + "", + "", + "", + "", + /* IOM 3 */ + "", + "", + "", + "", + /* IOM 4 */ + "", + "", + "", + "" +}; + + +static const char *const s6100_vsen_label[] = { + /* CPU Board */ + "CPU XP3R3V_EARLY", + "CPU XP5R0V_CP", + "CPU XP3R3V_STD", + "CPU XP3R3V_CP ", + "CPU XP0R75V_VTT_A", + "CPU XP0R75V_VTT_B", + "CPU XP1R07V_CPU", + "CPU XP1R0V_CPU", + "CPU XP12R0V", + "CPU VDDR_CPU_2", + "CPU VDDR_CPU_1", + "CPU XP1R5V_CLK", + "CPU XP1R35V_CPU", + "CPU XP1R8V_CPU", + "CPU XP1R0V_CPU_VNN", + "CPU XP1R0V_CPU_VCC", + "CPU XP1R5V_EARLY", + /* Switch Board */ + "SW XP12R0V_MON", + "SW XP3R3V_MON", + "SW XP1R8V_MON", + "SW XP1R25V_MON", + "SW XP1R2V_MON", + "SW XP1R0V_SW_MON", + "SW XP1R0V_ROV_SW_MON", + "XR1R0V_BCM84752_MON", + "SW XP5V_MB_MON", + "SW XP1R8V_FPGA_MON", + "SW XP3R3V_FPGA_MON", + + /* PSU */ + "PSU1 VIN", + "PSU1 VOUT", + "PSU2 VIN", + "PSU2 VOUT", + + /* IOM 1 */ + "IOM 1 #1", + "IOM 1 #2", + "IOM 1 #3", + "IOM 1 #4", + /* IOM 2 */ + "IOM 2 #1", + "IOM 2 #2", + "IOM 2 #3", + "IOM 2 #4", + /* IOM 1 */ + "IOM 3 #1", + "IOM 3 #2", + "IOM 3 #3", + "IOM 3 #4", + /* IOM 1 */ + "IOM 4 #1", + "IOM 4 #2", + "IOM 4 #3", + "IOM 4 #4" +}; + + +static const char *const z9100_curr_label[] = { + "XP1R0V", + "XP1R0V_ROV" +}; + + +static const char *const s6100_fan_label[] = { + "Tray1 Fan1", + "", + "Tray2 Fan1", + "", + "Tray3 Fan1", + "", + "Tray4 Fan1", + "", + "", + "", + "Psu1 Fan", + "Psu2 Fan" +}; + +static const char *const z9100_fan_label[] = { + "Tray1 Fan1", + "Tray1 Fan2", + "Tray2 Fan1", + "Tray2 Fan2", + "Tray3 Fan1", + "Tray3 Fan2", + "Tray4 Fan1", + "Tray4 Fan2", + "Tray5 Fan1", + "Tray5 Fan2", + "Psu1 Fan", + "Psu2 Fan" +}; + + +static const char *const s6100_psu_label[] = { + "Psu1 Input", + "Psu1 Output", + "Psu2 Input", + "Psu2 Output", +}; + +static const struct smf_devices smf_devices[] = { + [z9100smf] = { + .name = "SMF_Z9100_ON", + .tcpu_mask=0xe1ff, + .vsen_mask=0xfffdffff, + .curr_mask=0x3f, + .fanin_mask=0xfff, + .psu_mask=0xf, + .temp_label = z9100_temp_label, + .vsen_label = z9100_vsen_label, + .curr_label = z9100_curr_label, + .fan_label = z9100_fan_label, + .psu_label = s6100_psu_label + }, + [s6100smf] = { + .name = "SMF_S6100_ON", + .tcpu_mask=0x7fff, + .vsen_mask=0xfffffefdffff, + .curr_mask=0x3f, + .fanin_mask=0xc55, + .psu_mask=0xf, + .temp_label = s6100_temp_label, + .vsen_label = s6100_vsen_label, + .curr_label = z9100_curr_label, + .fan_label = s6100_fan_label, + .psu_label = s6100_psu_label + + } +}; + + +/* + * For each registered chip, we need to keep some data in memory. + * The structure is dynamically allocated. + */ +struct smf_data { + enum kinds kind; /* Inherited from SuperIO kind */ + unsigned short addr; + struct device *hwmon_dev; + struct mutex lock; + u64 tcpu_mask; + u64 vsen_mask; + u32 curr_mask; + u64 fanin_mask; + u64 psu_mask; + const char * const *temp_label; + const char * const *vsen_label; + const char * const *curr_label; + const char * const *fan_label; + const char * const *psu_label; +}; + + +struct smf_sio_data { + int sioreg; + enum kinds kind; +}; + + +static int smf_read_reg(struct smf_data *data, u16 reg) +{ + int res; + + mutex_lock(&data->lock); + outb_p(reg>> 8, data->addr + SMF_ADDR_REG_OFFSET); + outb_p(reg & 0xff, data->addr + SMF_ADDR_REG_OFFSET + 1); + res = inb_p(data->addr + SMF_READ_DATA_REG_OFFSET); + mutex_unlock(&data->lock); + return res; +} + + +static int smf_read_reg16(struct smf_data *data, u16 reg) +{ + int res; + + mutex_lock(&data->lock); + outb_p(reg>> 8, data->addr + SMF_ADDR_REG_OFFSET); + outb_p(reg & 0xff, data->addr + SMF_ADDR_REG_OFFSET + 1); + + res = inb_p(data->addr + SMF_READ_DATA_REG_OFFSET); + + outb_p((reg + 1)>> 8, data->addr + SMF_ADDR_REG_OFFSET); + outb_p((reg + 1) & 0xff, data->addr + SMF_ADDR_REG_OFFSET + 1); + + res = (res << 8) + inb_p(data->addr + SMF_READ_DATA_REG_OFFSET); + + mutex_unlock(&data->lock); + return res; +} + + +/* FANIN ATTR */ +static ssize_t +show_fan_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct smf_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(attr)->index; + return sprintf(buf, "%s\n", data->fan_label[nr]); +} + + +static ssize_t show_fan(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + int export_hex=0; + unsigned int ret = -1; + unsigned rpm; + + + if (index <10) + ret = smf_read_reg16(data, SMF_FAN_SPEED_ADDR + index * 2); + else switch (index) { + case 10: + ret = smf_read_reg16(data, PSU_1_FAN_SPEED); + break; + case 11: + ret = smf_read_reg16(data, PSU_2_FAN_SPEED); + break; + case 12: + ret = ~smf_read_reg(data, FAN_TRAY_PRESENCE); + export_hex = 1; + break; + + default: + return ret; + } + + + if (ret < 0) + return ret; + + rpm = ret; + + if(export_hex) + return sprintf(buf, "%x\n", rpm); + else + return sprintf(buf, "%u\n", rpm); +} + + +static ssize_t show_fan_fault(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + int ret=1, fan_status; + + index = index / 2; + fan_status = ~smf_read_reg(data, FAN_TRAY_PRESENCE); + + if (fan_status & (1 << (index))) + ret=0; + + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + + +static ssize_t show_fan_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + int ret, psu_fan_status=0; + + if(index < 2) + psu_fan_status = smf_read_reg(data, FAN_STATUS_GROUP_B); + + if (psu_fan_status & (1 << (index))) + ret=0; + + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + + +static ssize_t show_fan_airflow(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + int ret=1, fan_airflow; + + if (data->kind == s6100smf && index == FAN_TRAY_5) + return 0; + + fan_airflow = smf_read_reg(data, FAN_TRAY_AIRFLOW); + + if (fan_airflow & (1 << (index))) + ret=1; + + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + + +static ssize_t show_psu_fan(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + int ret=0, fan_status; + + if (index < FAN_601_FAULT){ + fan_status = smf_read_reg(data, PSU_1_FAN_STATUS); + ret = fan_status & (1 << index); + + } + else{ + fan_status = smf_read_reg(data, PSU_2_FAN_STATUS); + ret = fan_status & (1 << (index - 3)); + } + + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + + + +static umode_t smf_fanin_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct smf_data *data = dev_get_drvdata(dev); + + if (data->fanin_mask & (1 << (n % FANIN_MAX))) + return a->mode; + + return 0; +} + +static umode_t smf_dell_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + return a->mode; + +} + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan, NULL, 7); +static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, show_fan, NULL, 8); +static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, show_fan, NULL,9); +/* PSU1 FAN */ +static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, show_fan, NULL, 10); +/* PSU2 FAN */ +static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, show_fan, NULL, 11); + +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_fan_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_fan_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_alarm, S_IRUGO, show_fan_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_alarm, S_IRUGO, show_fan_alarm, NULL, 7); +static SENSOR_DEVICE_ATTR(fan9_alarm, S_IRUGO, show_fan_alarm, NULL, 8); +static SENSOR_DEVICE_ATTR(fan10_alarm, S_IRUGO, show_fan_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(fan11_alarm, S_IRUGO, show_psu_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan12_alarm, S_IRUGO, show_psu_fan, NULL, 4); + +static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_fan_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, show_fan_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, show_fan_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, show_fan_fault, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, show_fan_fault, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, show_fan_fault, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, show_fan_fault, NULL, 7); +static SENSOR_DEVICE_ATTR(fan9_fault, S_IRUGO, show_fan_fault, NULL, 8); +static SENSOR_DEVICE_ATTR(fan10_fault, S_IRUGO, show_fan_fault, NULL, 9); +static SENSOR_DEVICE_ATTR(fan11_fault, S_IRUGO, show_psu_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan12_fault, S_IRUGO, show_psu_fan, NULL, 5); + +static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, show_fan_label, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, show_fan_label, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_label, S_IRUGO, show_fan_label, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_label, S_IRUGO, show_fan_label, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_label, S_IRUGO, show_fan_label, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_label, S_IRUGO, show_fan_label, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_label, S_IRUGO, show_fan_label, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_label, S_IRUGO, show_fan_label, NULL, 7); +static SENSOR_DEVICE_ATTR(fan9_label, S_IRUGO, show_fan_label, NULL, 8); +static SENSOR_DEVICE_ATTR(fan10_label, S_IRUGO, show_fan_label, NULL, 9); +static SENSOR_DEVICE_ATTR(fan11_label, S_IRUGO, show_fan_label, NULL, 10); +static SENSOR_DEVICE_ATTR(fan12_label, S_IRUGO, show_fan_label, NULL, 11); + + +static struct attribute *smf_fanin_attrs[] = { + &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_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan8_input.dev_attr.attr, + &sensor_dev_attr_fan9_input.dev_attr.attr, + &sensor_dev_attr_fan10_input.dev_attr.attr, + &sensor_dev_attr_fan11_input.dev_attr.attr, + &sensor_dev_attr_fan12_input.dev_attr.attr, + + &sensor_dev_attr_fan1_label.dev_attr.attr, + &sensor_dev_attr_fan2_label.dev_attr.attr, + &sensor_dev_attr_fan3_label.dev_attr.attr, + &sensor_dev_attr_fan4_label.dev_attr.attr, + &sensor_dev_attr_fan5_label.dev_attr.attr, + &sensor_dev_attr_fan6_label.dev_attr.attr, + &sensor_dev_attr_fan7_label.dev_attr.attr, + &sensor_dev_attr_fan8_label.dev_attr.attr, + &sensor_dev_attr_fan9_label.dev_attr.attr, + &sensor_dev_attr_fan10_label.dev_attr.attr, + &sensor_dev_attr_fan11_label.dev_attr.attr, + &sensor_dev_attr_fan12_label.dev_attr.attr, + + + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, + &sensor_dev_attr_fan4_alarm.dev_attr.attr, + &sensor_dev_attr_fan5_alarm.dev_attr.attr, + &sensor_dev_attr_fan6_alarm.dev_attr.attr, + &sensor_dev_attr_fan7_alarm.dev_attr.attr, + &sensor_dev_attr_fan8_alarm.dev_attr.attr, + &sensor_dev_attr_fan9_alarm.dev_attr.attr, + &sensor_dev_attr_fan10_alarm.dev_attr.attr, + &sensor_dev_attr_fan11_alarm.dev_attr.attr, + &sensor_dev_attr_fan12_alarm.dev_attr.attr, + + &sensor_dev_attr_fan1_fault.dev_attr.attr, + &sensor_dev_attr_fan2_fault.dev_attr.attr, + &sensor_dev_attr_fan3_fault.dev_attr.attr, + &sensor_dev_attr_fan4_fault.dev_attr.attr, + &sensor_dev_attr_fan5_fault.dev_attr.attr, + &sensor_dev_attr_fan6_fault.dev_attr.attr, + &sensor_dev_attr_fan7_fault.dev_attr.attr, + &sensor_dev_attr_fan8_fault.dev_attr.attr, + &sensor_dev_attr_fan9_fault.dev_attr.attr, + &sensor_dev_attr_fan10_fault.dev_attr.attr, + &sensor_dev_attr_fan11_fault.dev_attr.attr, + &sensor_dev_attr_fan12_fault.dev_attr.attr, + + NULL +}; + + + + +/* VSEN ATTR */ +static ssize_t +show_voltage_label(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct smf_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(attr)->index; + return sprintf(buf, "%s\n", data->vsen_label[nr]); +} + + +static ssize_t show_voltage(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + int volt, ret=0; + int export_hex=0; + + /*0 to 27 */ + if (index < IN28_INPUT) /* Voltage sensors */ + ret = smf_read_reg16(data, CPU_1_VOLTAGE + index * 2); + else if ((data->kind == s6100smf) && (index < IN404_INPUT)) + ret = smf_read_reg16(data, IO_MODULE_1_VOLTAGE + index * 2); + else if ((data->kind == s6100smf) && (index < IOM_PRESENCE)) + ret = smf_read_reg(data, IO_MODULE_STATUS); + else if ((data->kind == s6100smf) && (index < IOM_PRESENCE_MAX)) + ret = smf_read_reg(data, IO_MODULE_PRESENCE); + + if (ret < 0) + return ret; + + if(index < 44) + volt = ret*10; + else + export_hex=1; + + if(export_hex) + return sprintf(buf, "%x\n", ret); + else + return sprintf(buf, "%d\n", volt); +} + + +static ssize_t show_psu_voltage(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + int ret; + + if (index < 2) /* PSU1 */ + ret = smf_read_reg16(data, PSU_1_INPUT_VOLTAGE + index * 2); + else /* PSU2 */ + ret = smf_read_reg16(data, PSU_2_INPUT_VOLTAGE + ((index - 2) * 2)); + + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret*10); +} + + +static ssize_t show_voltage_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + unsigned status=0; + int ret; + + if (index < 8) { + + if (data->kind == s6100smf) + status = smf_read_reg16(data, CPU_1_MONITOR_STATUS); + else + status = smf_read_reg16(data, CPU_5_MONITOR_STATUS); + + ret = status & (1 << index); + } + else if (index < 15) { + + if (data->kind == s6100smf) + ret = smf_read_reg16(data, CPU_2_MONITOR_STATUS); + else + ret = smf_read_reg16(data, CPU_6_MONITOR_STATUS); + + ret = status & (1 << index); + } + else if (index < 23) { + + if (data->kind == s6100smf) + ret = smf_read_reg16(data, CPU_3_MONITOR_STATUS); + else + ret = smf_read_reg16(data, CPU_7_MONITOR_STATUS); + + ret = status & (1 << index); + } + else { + + if (data->kind == s6100smf) + ret = smf_read_reg16(data, CPU_4_MONITOR_STATUS); + else + ret = smf_read_reg16(data, CPU_8_MONITOR_STATUS); + + ret = status & (1 << index); + } + + + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + + +static umode_t smf_vsen_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct smf_data *data = dev_get_drvdata(dev); + + if (data->vsen_mask & (1 << (n % VSEN_MAX))) + return a->mode; + return 0; +} + +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_voltage, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_voltage, NULL, 3); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 4); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 5); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 6); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_voltage, NULL, 7); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_voltage, NULL, 8); +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_voltage, NULL, 9); +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_voltage, NULL, 10); +static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, show_voltage, NULL, 11); +static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, show_voltage, NULL, 12); +static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, show_voltage, NULL, 13); +static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, show_voltage, NULL, 14); +static SENSOR_DEVICE_ATTR(in16_input, S_IRUGO, show_voltage, NULL, 15); +static SENSOR_DEVICE_ATTR(in17_input, S_IRUGO, show_voltage, NULL, 16); +static SENSOR_DEVICE_ATTR(in18_input, S_IRUGO, show_voltage, NULL, 17); +static SENSOR_DEVICE_ATTR(in19_input, S_IRUGO, show_voltage, NULL, 18); +static SENSOR_DEVICE_ATTR(in20_input, S_IRUGO, show_voltage, NULL, 19); +static SENSOR_DEVICE_ATTR(in21_input, S_IRUGO, show_voltage, NULL, 20); +static SENSOR_DEVICE_ATTR(in22_input, S_IRUGO, show_voltage, NULL, 21); +static SENSOR_DEVICE_ATTR(in23_input, S_IRUGO, show_voltage, NULL, 22); +static SENSOR_DEVICE_ATTR(in24_input, S_IRUGO, show_voltage, NULL, 23); +static SENSOR_DEVICE_ATTR(in25_input, S_IRUGO, show_voltage, NULL, 24); +static SENSOR_DEVICE_ATTR(in26_input, S_IRUGO, show_voltage, NULL, 25); +static SENSOR_DEVICE_ATTR(in27_input, S_IRUGO, show_voltage, NULL, 26); +static SENSOR_DEVICE_ATTR(in28_input, S_IRUGO, show_voltage, NULL, 27); + +/* PSU1 Voltage*/ +static SENSOR_DEVICE_ATTR(in29_input, S_IRUGO, show_psu_voltage, NULL, 0); +static SENSOR_DEVICE_ATTR(in30_input, S_IRUGO, show_psu_voltage, NULL, 1); + +/* PSU2 Voltage*/ +static SENSOR_DEVICE_ATTR(in31_input, S_IRUGO, show_psu_voltage, NULL, 2); +static SENSOR_DEVICE_ATTR(in32_input, S_IRUGO, show_psu_voltage, NULL, 3); + +/*IO Modules Voltage*/ +static SENSOR_DEVICE_ATTR(in101_input, S_IRUGO, show_voltage, NULL, 28); +static SENSOR_DEVICE_ATTR(in102_input, S_IRUGO, show_voltage, NULL, 29); +static SENSOR_DEVICE_ATTR(in103_input, S_IRUGO, show_voltage, NULL, 30); +static SENSOR_DEVICE_ATTR(in104_input, S_IRUGO, show_voltage, NULL, 31); +static SENSOR_DEVICE_ATTR(in201_input, S_IRUGO, show_voltage, NULL, 32); +static SENSOR_DEVICE_ATTR(in202_input, S_IRUGO, show_voltage, NULL, 33); +static SENSOR_DEVICE_ATTR(in203_input, S_IRUGO, show_voltage, NULL, 34); +static SENSOR_DEVICE_ATTR(in204_input, S_IRUGO, show_voltage, NULL, 35); +static SENSOR_DEVICE_ATTR(in301_input, S_IRUGO, show_voltage, NULL, 36); +static SENSOR_DEVICE_ATTR(in302_input, S_IRUGO, show_voltage, NULL, 37); +static SENSOR_DEVICE_ATTR(in303_input, S_IRUGO, show_voltage, NULL, 38); +static SENSOR_DEVICE_ATTR(in304_input, S_IRUGO, show_voltage, NULL, 39); +static SENSOR_DEVICE_ATTR(in401_input, S_IRUGO, show_voltage, NULL, 40); +static SENSOR_DEVICE_ATTR(in402_input, S_IRUGO, show_voltage, NULL, 41); +static SENSOR_DEVICE_ATTR(in403_input, S_IRUGO, show_voltage, NULL, 42); +static SENSOR_DEVICE_ATTR(in404_input, S_IRUGO, show_voltage, NULL, 43); + + + +static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_voltage_label, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_voltage_label, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_voltage_label, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_voltage_label, NULL, 3); +static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_voltage_label, NULL, 4); +static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_voltage_label, NULL, 5); +static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_voltage_label, NULL, 6); +static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_voltage_label, NULL, 7); +static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_voltage_label, NULL, 8); +static SENSOR_DEVICE_ATTR(in10_label, S_IRUGO, show_voltage_label, NULL, 9); +static SENSOR_DEVICE_ATTR(in11_label, S_IRUGO, show_voltage_label, NULL, 10); +static SENSOR_DEVICE_ATTR(in12_label, S_IRUGO, show_voltage_label, NULL, 11); +static SENSOR_DEVICE_ATTR(in13_label, S_IRUGO, show_voltage_label, NULL, 12); +static SENSOR_DEVICE_ATTR(in14_label, S_IRUGO, show_voltage_label, NULL, 13); +static SENSOR_DEVICE_ATTR(in15_label, S_IRUGO, show_voltage_label, NULL, 14); +static SENSOR_DEVICE_ATTR(in16_label, S_IRUGO, show_voltage_label, NULL, 15); +static SENSOR_DEVICE_ATTR(in17_label, S_IRUGO, show_voltage_label, NULL, 16); +static SENSOR_DEVICE_ATTR(in18_label, S_IRUGO, show_voltage_label, NULL, 17); +static SENSOR_DEVICE_ATTR(in19_label, S_IRUGO, show_voltage_label, NULL, 18); +static SENSOR_DEVICE_ATTR(in20_label, S_IRUGO, show_voltage_label, NULL, 19); +static SENSOR_DEVICE_ATTR(in21_label, S_IRUGO, show_voltage_label, NULL, 20); +static SENSOR_DEVICE_ATTR(in22_label, S_IRUGO, show_voltage_label, NULL, 21); +static SENSOR_DEVICE_ATTR(in23_label, S_IRUGO, show_voltage_label, NULL, 22); +static SENSOR_DEVICE_ATTR(in24_label, S_IRUGO, show_voltage_label, NULL, 23); +static SENSOR_DEVICE_ATTR(in25_label, S_IRUGO, show_voltage_label, NULL, 24); +static SENSOR_DEVICE_ATTR(in26_label, S_IRUGO, show_voltage_label, NULL, 25); +static SENSOR_DEVICE_ATTR(in27_label, S_IRUGO, show_voltage_label, NULL, 26); +static SENSOR_DEVICE_ATTR(in28_label, S_IRUGO, show_voltage_label, NULL, 27); + +/* PSU1 Voltage Label*/ +static SENSOR_DEVICE_ATTR(in29_label, S_IRUGO, show_voltage_label, NULL, 28); +static SENSOR_DEVICE_ATTR(in30_label, S_IRUGO, show_voltage_label, NULL, 29); + +/* PSU2 Voltage Label*/ +static SENSOR_DEVICE_ATTR(in31_label, S_IRUGO, show_voltage_label, NULL, 30); +static SENSOR_DEVICE_ATTR(in32_label, S_IRUGO, show_voltage_label, NULL, 31); + +/*IO Modules Labels*/ +static SENSOR_DEVICE_ATTR(in101_label, S_IRUGO, show_voltage_label, NULL, 32); +static SENSOR_DEVICE_ATTR(in102_label, S_IRUGO, show_voltage_label, NULL, 33); +static SENSOR_DEVICE_ATTR(in103_label, S_IRUGO, show_voltage_label, NULL, 34); +static SENSOR_DEVICE_ATTR(in104_label, S_IRUGO, show_voltage_label, NULL, 35); +static SENSOR_DEVICE_ATTR(in201_label, S_IRUGO, show_voltage_label, NULL, 36); +static SENSOR_DEVICE_ATTR(in202_label, S_IRUGO, show_voltage_label, NULL, 37); +static SENSOR_DEVICE_ATTR(in203_label, S_IRUGO, show_voltage_label, NULL, 38); +static SENSOR_DEVICE_ATTR(in204_label, S_IRUGO, show_voltage_label, NULL, 39); +static SENSOR_DEVICE_ATTR(in301_label, S_IRUGO, show_voltage_label, NULL, 40); +static SENSOR_DEVICE_ATTR(in302_label, S_IRUGO, show_voltage_label, NULL, 41); +static SENSOR_DEVICE_ATTR(in303_label, S_IRUGO, show_voltage_label, NULL, 42); +static SENSOR_DEVICE_ATTR(in304_label, S_IRUGO, show_voltage_label, NULL, 43); +static SENSOR_DEVICE_ATTR(in401_label, S_IRUGO, show_voltage_label, NULL, 44); +static SENSOR_DEVICE_ATTR(in402_label, S_IRUGO, show_voltage_label, NULL, 45); +static SENSOR_DEVICE_ATTR(in403_label, S_IRUGO, show_voltage_label, NULL, 46); +static SENSOR_DEVICE_ATTR(in404_label, S_IRUGO, show_voltage_label, NULL, 47); + + + +/* CPU Voltage Alarm */ +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_voltage_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_voltage_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_voltage_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_voltage_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_voltage_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_voltage_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_voltage_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_voltage_alarm, NULL, 7); + +static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_voltage_alarm, NULL, 8); +static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_voltage_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(in11_alarm, S_IRUGO, show_voltage_alarm, NULL, 10); +static SENSOR_DEVICE_ATTR(in12_alarm, S_IRUGO, show_voltage_alarm, NULL, 11); +static SENSOR_DEVICE_ATTR(in13_alarm, S_IRUGO, show_voltage_alarm, NULL, 12); +static SENSOR_DEVICE_ATTR(in14_alarm, S_IRUGO, show_voltage_alarm, NULL, 13); +static SENSOR_DEVICE_ATTR(in15_alarm, S_IRUGO, show_voltage_alarm, NULL, 14); +static SENSOR_DEVICE_ATTR(in16_alarm, S_IRUGO, show_voltage_alarm, NULL, 15); + +static SENSOR_DEVICE_ATTR(in17_alarm, S_IRUGO, show_voltage_alarm, NULL, 16); +static SENSOR_DEVICE_ATTR(in18_alarm, S_IRUGO, show_voltage_alarm, NULL, 17); +static SENSOR_DEVICE_ATTR(in19_alarm, S_IRUGO, show_voltage_alarm, NULL, 18); +static SENSOR_DEVICE_ATTR(in20_alarm, S_IRUGO, show_voltage_alarm, NULL, 19); +static SENSOR_DEVICE_ATTR(in21_alarm, S_IRUGO, show_voltage_alarm, NULL, 20); +static SENSOR_DEVICE_ATTR(in22_alarm, S_IRUGO, show_voltage_alarm, NULL, 21); +static SENSOR_DEVICE_ATTR(in23_alarm, S_IRUGO, show_voltage_alarm, NULL, 22); +static SENSOR_DEVICE_ATTR(in24_alarm, S_IRUGO, show_voltage_alarm, NULL, 23); + +static SENSOR_DEVICE_ATTR(in25_alarm, S_IRUGO, show_voltage_alarm, NULL, 24); +static SENSOR_DEVICE_ATTR(in26_alarm, S_IRUGO, show_voltage_alarm, NULL, 25); +static SENSOR_DEVICE_ATTR(in27_alarm, S_IRUGO, show_voltage_alarm, NULL, 26); +static SENSOR_DEVICE_ATTR(in28_alarm, S_IRUGO, show_voltage_alarm, NULL, 27); + + + +static struct attribute *smf_vsen_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + &sensor_dev_attr_in12_input.dev_attr.attr, + &sensor_dev_attr_in13_input.dev_attr.attr, + &sensor_dev_attr_in14_input.dev_attr.attr, + &sensor_dev_attr_in15_input.dev_attr.attr, + &sensor_dev_attr_in16_input.dev_attr.attr, + &sensor_dev_attr_in17_input.dev_attr.attr, + &sensor_dev_attr_in18_input.dev_attr.attr, + &sensor_dev_attr_in19_input.dev_attr.attr, + &sensor_dev_attr_in20_input.dev_attr.attr, + &sensor_dev_attr_in21_input.dev_attr.attr, + &sensor_dev_attr_in22_input.dev_attr.attr, + &sensor_dev_attr_in23_input.dev_attr.attr, + &sensor_dev_attr_in24_input.dev_attr.attr, + &sensor_dev_attr_in25_input.dev_attr.attr, + &sensor_dev_attr_in26_input.dev_attr.attr, + &sensor_dev_attr_in27_input.dev_attr.attr, + &sensor_dev_attr_in28_input.dev_attr.attr, + + &sensor_dev_attr_in29_input.dev_attr.attr, + &sensor_dev_attr_in30_input.dev_attr.attr, + &sensor_dev_attr_in31_input.dev_attr.attr, + &sensor_dev_attr_in32_input.dev_attr.attr, + + &sensor_dev_attr_in101_input.dev_attr.attr, + &sensor_dev_attr_in102_input.dev_attr.attr, + &sensor_dev_attr_in103_input.dev_attr.attr, + &sensor_dev_attr_in104_input.dev_attr.attr, + &sensor_dev_attr_in201_input.dev_attr.attr, + &sensor_dev_attr_in202_input.dev_attr.attr, + &sensor_dev_attr_in203_input.dev_attr.attr, + &sensor_dev_attr_in204_input.dev_attr.attr, + &sensor_dev_attr_in301_input.dev_attr.attr, + &sensor_dev_attr_in302_input.dev_attr.attr, + &sensor_dev_attr_in303_input.dev_attr.attr, + &sensor_dev_attr_in304_input.dev_attr.attr, + &sensor_dev_attr_in401_input.dev_attr.attr, + &sensor_dev_attr_in402_input.dev_attr.attr, + &sensor_dev_attr_in403_input.dev_attr.attr, + &sensor_dev_attr_in404_input.dev_attr.attr, + + + &sensor_dev_attr_in1_label.dev_attr.attr, + &sensor_dev_attr_in2_label.dev_attr.attr, + &sensor_dev_attr_in3_label.dev_attr.attr, + &sensor_dev_attr_in4_label.dev_attr.attr, + &sensor_dev_attr_in5_label.dev_attr.attr, + &sensor_dev_attr_in6_label.dev_attr.attr, + &sensor_dev_attr_in7_label.dev_attr.attr, + &sensor_dev_attr_in8_label.dev_attr.attr, + &sensor_dev_attr_in9_label.dev_attr.attr, + &sensor_dev_attr_in10_label.dev_attr.attr, + &sensor_dev_attr_in11_label.dev_attr.attr, + &sensor_dev_attr_in12_label.dev_attr.attr, + &sensor_dev_attr_in13_label.dev_attr.attr, + &sensor_dev_attr_in14_label.dev_attr.attr, + &sensor_dev_attr_in15_label.dev_attr.attr, + &sensor_dev_attr_in16_label.dev_attr.attr, + &sensor_dev_attr_in17_label.dev_attr.attr, + &sensor_dev_attr_in18_label.dev_attr.attr, + &sensor_dev_attr_in19_label.dev_attr.attr, + &sensor_dev_attr_in20_label.dev_attr.attr, + &sensor_dev_attr_in21_label.dev_attr.attr, + &sensor_dev_attr_in22_label.dev_attr.attr, + &sensor_dev_attr_in23_label.dev_attr.attr, + &sensor_dev_attr_in24_label.dev_attr.attr, + &sensor_dev_attr_in25_label.dev_attr.attr, + &sensor_dev_attr_in26_label.dev_attr.attr, + &sensor_dev_attr_in27_label.dev_attr.attr, + &sensor_dev_attr_in28_label.dev_attr.attr, + + &sensor_dev_attr_in29_label.dev_attr.attr, + &sensor_dev_attr_in30_label.dev_attr.attr, + &sensor_dev_attr_in31_label.dev_attr.attr, + &sensor_dev_attr_in32_label.dev_attr.attr, + + &sensor_dev_attr_in101_label.dev_attr.attr, + &sensor_dev_attr_in102_label.dev_attr.attr, + &sensor_dev_attr_in103_label.dev_attr.attr, + &sensor_dev_attr_in104_label.dev_attr.attr, + &sensor_dev_attr_in201_label.dev_attr.attr, + &sensor_dev_attr_in202_label.dev_attr.attr, + &sensor_dev_attr_in203_label.dev_attr.attr, + &sensor_dev_attr_in204_label.dev_attr.attr, + &sensor_dev_attr_in301_label.dev_attr.attr, + &sensor_dev_attr_in302_label.dev_attr.attr, + &sensor_dev_attr_in303_label.dev_attr.attr, + &sensor_dev_attr_in304_label.dev_attr.attr, + &sensor_dev_attr_in401_label.dev_attr.attr, + &sensor_dev_attr_in402_label.dev_attr.attr, + &sensor_dev_attr_in403_label.dev_attr.attr, + &sensor_dev_attr_in404_label.dev_attr.attr, + + + &sensor_dev_attr_in1_alarm.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, + &sensor_dev_attr_in7_alarm.dev_attr.attr, + &sensor_dev_attr_in8_alarm.dev_attr.attr, + &sensor_dev_attr_in9_alarm.dev_attr.attr, + &sensor_dev_attr_in10_alarm.dev_attr.attr, + &sensor_dev_attr_in11_alarm.dev_attr.attr, + &sensor_dev_attr_in12_alarm.dev_attr.attr, + &sensor_dev_attr_in13_alarm.dev_attr.attr, + &sensor_dev_attr_in14_alarm.dev_attr.attr, + &sensor_dev_attr_in15_alarm.dev_attr.attr, + &sensor_dev_attr_in16_alarm.dev_attr.attr, + &sensor_dev_attr_in17_alarm.dev_attr.attr, + &sensor_dev_attr_in18_alarm.dev_attr.attr, + &sensor_dev_attr_in19_alarm.dev_attr.attr, + &sensor_dev_attr_in20_alarm.dev_attr.attr, + &sensor_dev_attr_in21_alarm.dev_attr.attr, + &sensor_dev_attr_in22_alarm.dev_attr.attr, + &sensor_dev_attr_in23_alarm.dev_attr.attr, + &sensor_dev_attr_in24_alarm.dev_attr.attr, + &sensor_dev_attr_in25_alarm.dev_attr.attr, + &sensor_dev_attr_in26_alarm.dev_attr.attr, + &sensor_dev_attr_in27_alarm.dev_attr.attr, + &sensor_dev_attr_in28_alarm.dev_attr.attr, + + + NULL +}; + +static const struct attribute_group smf_vsen_group = { + .attrs = smf_vsen_attrs, + .is_visible = smf_vsen_is_visible, +}; + +/* CURRENT ATTR */ +static ssize_t +show_current_label(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct smf_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(attr)->index; + return sprintf(buf, "%s\n", data->curr_label[nr]); +} + +static ssize_t show_current(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + int ret=0; + int curr; + + if (index < CURR22_INPUT) + if (data->kind == s6100smf) + ret = smf_read_reg16(data, SWITCH_CURRENT_S6100 + index * 2); + else + ret = smf_read_reg16(data, SWITCH_CURRENT_Z9100 + index * 2); + else if (index < CURR602_INPUT) + curr = smf_read_reg16(data, PSU_1_INPUT_CURRENT + (index % 4) * 2); + else + curr = smf_read_reg16(data, PSU_2_INPUT_CURRENT + (index % 4) * 2); + + + if (ret < 0) + return ret; + + /* TODO: docs say 10mA, value look like A? */ + if(index < 2) + curr = ret*1000; + + return sprintf(buf, "%d\n", curr); +} + + +static umode_t smf_curr_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct smf_data *data = dev_get_drvdata(dev); + + if (data->curr_mask & (1 << (n % CURR_MAX))) + return a->mode; + return 0; +} + + +static SENSOR_DEVICE_ATTR(curr21_input, S_IRUGO, show_current, NULL, 0); +static SENSOR_DEVICE_ATTR(curr22_input, S_IRUGO, show_current, NULL, 1); + +static SENSOR_DEVICE_ATTR(curr601_input, S_IRUGO, show_current, NULL, 2); +static SENSOR_DEVICE_ATTR(curr602_input, S_IRUGO, show_current, NULL, 3); + +static SENSOR_DEVICE_ATTR(curr701_input, S_IRUGO, show_current, NULL, 4); +static SENSOR_DEVICE_ATTR(curr702_input, S_IRUGO, show_current, NULL, 5); + +static SENSOR_DEVICE_ATTR(curr21_label, S_IRUGO, show_current_label, NULL, 0); +static SENSOR_DEVICE_ATTR(curr22_label, S_IRUGO, show_current_label, NULL, 1); + +static SENSOR_DEVICE_ATTR(curr601_label, S_IRUGO, show_current_label, NULL, 2); +static SENSOR_DEVICE_ATTR(curr602_label, S_IRUGO, show_current_label, NULL, 3); + +static SENSOR_DEVICE_ATTR(curr701_label, S_IRUGO, show_current_label, NULL, 4); +static SENSOR_DEVICE_ATTR(curr702_label, S_IRUGO, show_current_label, NULL, 5); + + +static struct attribute *smf_curr_attrs[] = { + &sensor_dev_attr_curr21_input.dev_attr.attr, + &sensor_dev_attr_curr22_input.dev_attr.attr, + + &sensor_dev_attr_curr601_input.dev_attr.attr, + &sensor_dev_attr_curr602_input.dev_attr.attr, + + &sensor_dev_attr_curr701_input.dev_attr.attr, + &sensor_dev_attr_curr702_input.dev_attr.attr, + + &sensor_dev_attr_curr21_label.dev_attr.attr, + &sensor_dev_attr_curr22_label.dev_attr.attr, + + &sensor_dev_attr_curr601_label.dev_attr.attr, + &sensor_dev_attr_curr602_label.dev_attr.attr, + + &sensor_dev_attr_curr701_label.dev_attr.attr, + &sensor_dev_attr_curr702_label.dev_attr.attr, + + NULL +}; + + +static const struct attribute_group smf_curr_group = { + .attrs = smf_curr_attrs, + .is_visible = smf_curr_is_visible, +}; + + +/* CPU_TEMP ATTR */ +static ssize_t +show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct smf_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(attr)->index; + return sprintf(buf, "%s\n", data->temp_label[nr]); +} + + +static ssize_t show_tcpu(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + int ret; + int temp; + + if (index < TEMP13_INPUT) /* Temp sensors */ + ret = smf_read_reg16(data, TEMP_SENSOR_1 + index * 2); + else if(index < TEMP601_INPUT) + ret = smf_read_reg16(data, PSU_1_TEMPERATURE); + else + ret = smf_read_reg16(data, PSU_2_TEMPERATURE); + + if (ret < 0) + return ret; + + if (ret > 65500) + ret = 0; + + if (ret & 0x8000) + ret = - (ret & 0x7fff); + + temp = ret*100; + + return sprintf(buf, "%d\n", temp); +} + + +static ssize_t show_temp_crit(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + int ret; + int temp; + + ret = smf_read_reg16(data, TEMP_SENSOR_1_HW_LIMIT + index * 2); + if (ret < 0) + return ret; + + if (ret == 65535) + ret = 0; + + if (ret & 0x8000) + ret = - (ret & 0x7fff); + + temp = ret*100; + + return sprintf(buf, "%d\n", temp); +} + + +/*static ssize_t show_temp_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + int ret; + int temp; + + ret = smf_read_reg16(data, TEMP_SENSOR_1_STATUS + index * 2); + + if (ret < 0) + return ret; + + temp = ret; + + return sprintf(buf, "%d\n", temp); +}*/ + + +static umode_t smf_tcpu_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct smf_data *data = dev_get_drvdata(dev); + + if (data->tcpu_mask & (1 << (n % TCPU_MAX))) + return a->mode; + + return 0; +} + + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_tcpu, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_tcpu, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_tcpu, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_tcpu, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_tcpu, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_tcpu, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_tcpu, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_tcpu, NULL, 7); +static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_tcpu, NULL, 8); +static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO, show_tcpu, NULL, 9); +static SENSOR_DEVICE_ATTR(temp11_input, S_IRUGO, show_tcpu, NULL, 10); +static SENSOR_DEVICE_ATTR(temp12_input, S_IRUGO, show_tcpu, NULL, 11); +static SENSOR_DEVICE_ATTR(temp13_input, S_IRUGO, show_tcpu, NULL, 12); + +/* PSU1 Fan Temp */ +static SENSOR_DEVICE_ATTR(temp14_input, S_IRUGO, show_tcpu, NULL, 13); + +/* PSU2 Fan Temp */ +static SENSOR_DEVICE_ATTR(temp15_input, S_IRUGO, show_tcpu, NULL, 14); + +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO, show_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_label, S_IRUGO, show_temp_label, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_label, S_IRUGO, show_temp_label, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, show_temp_label, NULL, 7); +static SENSOR_DEVICE_ATTR(temp9_label, S_IRUGO, show_temp_label, NULL, 8); +static SENSOR_DEVICE_ATTR(temp10_label, S_IRUGO, show_temp_label, NULL, 9); +static SENSOR_DEVICE_ATTR(temp11_label, S_IRUGO, show_temp_label, NULL, 10); +static SENSOR_DEVICE_ATTR(temp12_label, S_IRUGO, show_temp_label, NULL, 11); +static SENSOR_DEVICE_ATTR(temp13_label, S_IRUGO, show_temp_label, NULL, 12); + +static SENSOR_DEVICE_ATTR(temp14_label, S_IRUGO, show_temp_label, NULL, 14); +static SENSOR_DEVICE_ATTR(temp15_label, S_IRUGO, show_temp_label, NULL, 15); + + +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit, NULL, 5); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit, NULL, 9); +static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp_crit, NULL, 13); +static SENSOR_DEVICE_ATTR(temp5_crit, S_IRUGO, show_temp_crit, NULL, 17); +static SENSOR_DEVICE_ATTR(temp6_crit, S_IRUGO, show_temp_crit, NULL, 21); +static SENSOR_DEVICE_ATTR(temp7_crit, S_IRUGO, show_temp_crit, NULL, 25); +static SENSOR_DEVICE_ATTR(temp8_crit, S_IRUGO, show_temp_crit, NULL, 29); +static SENSOR_DEVICE_ATTR(temp9_crit, S_IRUGO, show_temp_crit, NULL, 33); +static SENSOR_DEVICE_ATTR(temp10_crit, S_IRUGO, show_temp_crit, NULL, 37); +static SENSOR_DEVICE_ATTR(temp11_crit, S_IRUGO, show_temp_crit, NULL, 41); +static SENSOR_DEVICE_ATTR(temp12_crit, S_IRUGO, show_temp_crit, NULL, 45); +static SENSOR_DEVICE_ATTR(temp13_crit, S_IRUGO, show_temp_crit, NULL, 49); + +static SENSOR_DEVICE_ATTR(temp14_crit, S_IRUGO, show_temp_crit, NULL, 11); +static SENSOR_DEVICE_ATTR(temp15_crit, S_IRUGO, show_temp_crit, NULL, 11); + + +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_crit, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_crit, NULL, 6); +static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_crit, NULL, 10); +static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_crit, NULL, 14); +static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_crit, NULL, 18); +static SENSOR_DEVICE_ATTR(temp6_max, S_IRUGO, show_temp_crit, NULL, 22); +static SENSOR_DEVICE_ATTR(temp7_max, S_IRUGO, show_temp_crit, NULL, 26); +static SENSOR_DEVICE_ATTR(temp8_max, S_IRUGO, show_temp_crit, NULL, 30); +static SENSOR_DEVICE_ATTR(temp9_max, S_IRUGO, show_temp_crit, NULL, 34); +static SENSOR_DEVICE_ATTR(temp10_max, S_IRUGO, show_temp_crit, NULL, 38); +static SENSOR_DEVICE_ATTR(temp11_max, S_IRUGO, show_temp_crit, NULL, 42); +static SENSOR_DEVICE_ATTR(temp12_max, S_IRUGO, show_temp_crit, NULL, 46); +static SENSOR_DEVICE_ATTR(temp13_max, S_IRUGO, show_temp_crit, NULL, 50); + +static SENSOR_DEVICE_ATTR(temp14_max, S_IRUGO, show_temp_crit, NULL, 46); +static SENSOR_DEVICE_ATTR(temp15_max, S_IRUGO, show_temp_crit, NULL, 50); + + +static struct attribute *smf_tcpu_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp9_input.dev_attr.attr, + &sensor_dev_attr_temp10_input.dev_attr.attr, + &sensor_dev_attr_temp11_input.dev_attr.attr, + &sensor_dev_attr_temp12_input.dev_attr.attr, + &sensor_dev_attr_temp13_input.dev_attr.attr, + &sensor_dev_attr_temp14_input.dev_attr.attr, + &sensor_dev_attr_temp15_input.dev_attr.attr, + + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp3_label.dev_attr.attr, + &sensor_dev_attr_temp4_label.dev_attr.attr, + &sensor_dev_attr_temp5_label.dev_attr.attr, + &sensor_dev_attr_temp6_label.dev_attr.attr, + &sensor_dev_attr_temp7_label.dev_attr.attr, + &sensor_dev_attr_temp8_label.dev_attr.attr, + &sensor_dev_attr_temp9_label.dev_attr.attr, + &sensor_dev_attr_temp10_label.dev_attr.attr, + &sensor_dev_attr_temp11_label.dev_attr.attr, + &sensor_dev_attr_temp12_label.dev_attr.attr, + &sensor_dev_attr_temp13_label.dev_attr.attr, + &sensor_dev_attr_temp14_label.dev_attr.attr, + &sensor_dev_attr_temp15_label.dev_attr.attr, + + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp5_crit.dev_attr.attr, + &sensor_dev_attr_temp6_crit.dev_attr.attr, + &sensor_dev_attr_temp7_crit.dev_attr.attr, + &sensor_dev_attr_temp8_crit.dev_attr.attr, + &sensor_dev_attr_temp9_crit.dev_attr.attr, + &sensor_dev_attr_temp10_crit.dev_attr.attr, + &sensor_dev_attr_temp11_crit.dev_attr.attr, + &sensor_dev_attr_temp12_crit.dev_attr.attr, + &sensor_dev_attr_temp13_crit.dev_attr.attr, + &sensor_dev_attr_temp14_crit.dev_attr.attr, + &sensor_dev_attr_temp15_crit.dev_attr.attr, + + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp6_max.dev_attr.attr, + &sensor_dev_attr_temp7_max.dev_attr.attr, + &sensor_dev_attr_temp8_max.dev_attr.attr, + &sensor_dev_attr_temp9_max.dev_attr.attr, + &sensor_dev_attr_temp10_max.dev_attr.attr, + &sensor_dev_attr_temp11_max.dev_attr.attr, + &sensor_dev_attr_temp12_max.dev_attr.attr, + &sensor_dev_attr_temp13_max.dev_attr.attr, + &sensor_dev_attr_temp14_max.dev_attr.attr, + &sensor_dev_attr_temp15_max.dev_attr.attr, + + NULL +}; + + +static const struct attribute_group smf_tcpu_group = { + .attrs = smf_tcpu_attrs, + .is_visible = smf_tcpu_is_visible, +}; + + +/* PSU ATTR */ +static ssize_t +show_psu_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct smf_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(attr)->index; + return sprintf(buf, "%s\n", data->psu_label[nr]); +} + + +static ssize_t show_psu(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + int ret=0, export_hex=0; + int psu_status=0, pow; + + switch (index) { + + case 0: + pow = smf_read_reg16(data, PSU_1_MAX_POWER); + /* TODO Fix */ + if (data->kind == s6100smf) + ret = 1000000 * 1100; + else + ret = 1000000 * 750; + break; + case 1: + ret = smf_read_reg(data, PSU_1_STATUS); + export_hex=1; + break; + case 2: + ret = 100000 * smf_read_reg16(data, PSU_1_INPUT_POWER); + break; + case 3: + ret = 100000 * smf_read_reg16(data, PSU_1_OUTPUT_POWER); + break; + case 4: + psu_status = smf_read_reg(data, PSU_1_STATUS); + if (psu_status &(1)) + ret=1; + break; + case 5: + pow = smf_read_reg16(data, PSU_2_MAX_POWER); + ret = 1000000 * pow; + /* TODO Fix */ + if (data->kind == s6100smf) + ret = 1000000 * 1100; + else + ret = 1000000 * 750; + break; + case 6: + ret = smf_read_reg(data, PSU_2_STATUS); + export_hex=1; + break; + case 7: + ret = 100000 * smf_read_reg16(data, PSU_2_INPUT_POWER); + break; + case 8: + ret = 100000 * smf_read_reg16(data, PSU_2_OUTPUT_POWER); + break; + case 9: + psu_status = smf_read_reg(data, PSU_2_STATUS); + if (psu_status &(1)) + ret=1; + break; + case 10: + pow = smf_read_reg16(data, CURRENT_TOTAL_POWER); + ret = pow/10; + break; + default: + return ret; + } + + if (ret < 0) + return ret; + + pow = ret; + + if(export_hex) + return sprintf(buf, "%x\n", pow); + else + return sprintf(buf, "%u\n", pow); +} + + +static umode_t smf_psu_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct smf_data *data = dev_get_drvdata(dev); + + if (data->psu_mask & (1 << (n % PSU_MAX))) + return a->mode; + return 0; +} + +/* PSU */ +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, show_psu, NULL, 2); +static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, show_psu, NULL, 3); +static SENSOR_DEVICE_ATTR(power3_input, S_IRUGO, show_psu, NULL, 7); +static SENSOR_DEVICE_ATTR(power4_input, S_IRUGO, show_psu, NULL, 8); + +static SENSOR_DEVICE_ATTR(power1_label, S_IRUGO, show_psu_label, NULL, 0); +static SENSOR_DEVICE_ATTR(power2_label, S_IRUGO, show_psu_label, NULL, 1); +static SENSOR_DEVICE_ATTR(power3_label, S_IRUGO, show_psu_label, NULL, 2); +static SENSOR_DEVICE_ATTR(power4_label, S_IRUGO, show_psu_label, NULL, 3); + +static SENSOR_DEVICE_ATTR(power1_max, S_IRUGO, show_psu, NULL, 0); +static SENSOR_DEVICE_ATTR(power2_max, S_IRUGO, show_psu, NULL, 0); +static SENSOR_DEVICE_ATTR(power3_max, S_IRUGO, show_psu, NULL, 5); +static SENSOR_DEVICE_ATTR(power4_max, S_IRUGO, show_psu, NULL, 5); + + +/* PSU2 */ +//static SENSOR_DEVICE_ATTR(power602_alarm, S_IRUGO, show_psu, NULL, 4); +//static SENSOR_DEVICE_ATTR(power702_alarm, S_IRUGO, show_psu, NULL, 9); + + + +static struct attribute *smf_psu_attrs[] = { + + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power2_input.dev_attr.attr, + &sensor_dev_attr_power3_input.dev_attr.attr, + &sensor_dev_attr_power4_input.dev_attr.attr, + + &sensor_dev_attr_power1_label.dev_attr.attr, + &sensor_dev_attr_power2_label.dev_attr.attr, + &sensor_dev_attr_power3_label.dev_attr.attr, + &sensor_dev_attr_power4_label.dev_attr.attr, + + &sensor_dev_attr_power1_max.dev_attr.attr, + &sensor_dev_attr_power2_max.dev_attr.attr, + &sensor_dev_attr_power3_max.dev_attr.attr, + &sensor_dev_attr_power4_max.dev_attr.attr, + + NULL +}; + + +static const struct attribute_group smf_psu_group = { + .attrs = smf_psu_attrs, + .is_visible = smf_psu_is_visible, +}; + + +static const struct attribute_group smf_fanin_group = { + .attrs = smf_fanin_attrs, + .is_visible = smf_fanin_is_visible, +}; + + +static SENSOR_DEVICE_ATTR(fan_tray_presence, S_IRUGO, show_fan, NULL, 12); +static SENSOR_DEVICE_ATTR(fan1_airflow, S_IRUGO, show_fan_airflow, NULL, 0); +static SENSOR_DEVICE_ATTR(fan3_airflow, S_IRUGO, show_fan_airflow, NULL, 1); +static SENSOR_DEVICE_ATTR(fan5_airflow, S_IRUGO, show_fan_airflow, NULL, 2); +static SENSOR_DEVICE_ATTR(fan7_airflow, S_IRUGO, show_fan_airflow, NULL, 3); +static SENSOR_DEVICE_ATTR(fan9_airflow, S_IRUGO, show_fan_airflow, NULL, 4); +static SENSOR_DEVICE_ATTR(fan11_airflow, S_IRUGO, show_psu_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan12_airflow, S_IRUGO, show_psu_fan, NULL, 3); +/* IOM status */ +static SENSOR_DEVICE_ATTR(iom_status, S_IRUGO, show_voltage, NULL, 44); +static SENSOR_DEVICE_ATTR(iom_presence, S_IRUGO, show_voltage, NULL, 45); + +static SENSOR_DEVICE_ATTR(psu1_presence, S_IRUGO, show_psu, NULL, 1); +static SENSOR_DEVICE_ATTR(psu2_presence, S_IRUGO, show_psu, NULL, 6); +static SENSOR_DEVICE_ATTR(current_total_power, S_IRUGO, show_psu, NULL, 10); + +static struct attribute *smf_dell_attrs[] = { + &sensor_dev_attr_fan_tray_presence.dev_attr.attr, + &sensor_dev_attr_fan1_airflow.dev_attr.attr, + &sensor_dev_attr_fan3_airflow.dev_attr.attr, + &sensor_dev_attr_fan5_airflow.dev_attr.attr, + &sensor_dev_attr_fan7_airflow.dev_attr.attr, + &sensor_dev_attr_fan9_airflow.dev_attr.attr, + &sensor_dev_attr_fan11_airflow.dev_attr.attr, + &sensor_dev_attr_fan12_airflow.dev_attr.attr, + &sensor_dev_attr_iom_status.dev_attr.attr, + &sensor_dev_attr_iom_presence.dev_attr.attr, + &sensor_dev_attr_psu1_presence.dev_attr.attr, + &sensor_dev_attr_psu2_presence.dev_attr.attr, + &sensor_dev_attr_current_total_power.dev_attr.attr, + NULL +}; + +static const struct attribute_group smf_dell_group = { + .attrs = smf_dell_attrs, + .is_visible = smf_dell_is_visible, +}; + + +static const struct attribute_group *smf_groups[] = { + &smf_psu_group, + &smf_fanin_group, + &smf_vsen_group, + &smf_curr_group, + &smf_tcpu_group, + &smf_dell_group, + NULL +}; + + + +static int smf_probe(struct platform_device *pdev) +{ + struct smf_data *data; + struct device *dev = &pdev->dev; + struct smf_sio_data *sio_data = dev_get_platdata(dev); + struct resource *res; + int err = 0; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!request_region(res->start, IOREGION_LENGTH, + smf_devices[sio_data->kind].name)) { + err = -EBUSY; + dev_err(dev, "Failed to request region 0x%lx-0x%lx\n", + (unsigned long)res->start, + (unsigned long)res->start + IOREGION_LENGTH - 1); + return err; + } + + data = devm_kzalloc(dev, sizeof(struct smf_data), GFP_KERNEL); + /* TODO Use probe address value */ + data->addr = SMF_PROBE_ADDR; + data->kind = sio_data->kind; + + if (!data) + return -ENOMEM; + + mutex_init(&data->lock); + + /* PSU attributes */ + data->psu_mask = smf_devices[data->kind].psu_mask; + data->psu_label = smf_devices[data->kind].psu_label; + + /* FANIN attributes */ + data->fanin_mask = smf_devices[data->kind].fanin_mask; + data->fan_label = smf_devices[data->kind].fan_label; + + /* VSEN attributes */ + data->vsen_mask = smf_devices[data->kind].vsen_mask; + data->vsen_label = smf_devices[data->kind].vsen_label; + + /* CURR attributes */ + data->curr_mask = smf_devices[data->kind].curr_mask; + data->curr_label = smf_devices[data->kind].curr_label; + + /* CPU_TEMP attributes */ + data->tcpu_mask = smf_devices[data->kind].tcpu_mask; + data->temp_label = smf_devices[data->kind].temp_label; + + data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, + smf_devices[data->kind].name, + data, smf_groups); + + return PTR_ERR_OR_ZERO(data->hwmon_dev); +} + + +static int smf_remove(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + release_region(res->start, IOREGION_LENGTH); + return 0; +} + + +static struct platform_driver smf_driver = { + .driver = { + .name = "SMF", + }, + .probe = smf_probe, + .remove = smf_remove +}; + +int __init +smf_find(int sioaddr, unsigned short *addr, struct smf_sio_data *sio_data) +{ + + int val; + + if (force_id) + val = force_id; + else + val = inb(sioaddr + SIO_REG_DEVID); + + switch (val) { + case SIO_Z9100_ID: + sio_data->kind = z9100smf; + break; + case SIO_S6100_ID: + sio_data->kind = s6100smf; + break; + + default: + if (val != 0xffff) + pr_debug("unsupported chip ID: 0x%04x\n", val); + return -ENODEV; + } + + /* TODO Use define, should this be 0x200 or 0x210??? */ + *addr = sioaddr; + + pr_info("Found %s chip at %#x\n", smf_devices[sio_data->kind].name, *addr); + sio_data->sioreg = sioaddr; + + return (0); +} + + +/* + * when Super-I/O functions move to a separate file, the Super-I/O + * bus will manage the lifetime of the device and this module will only keep + * track of the smf driver. But since we platform_device_alloc(), we + * must keep track of the device + */ +static struct platform_device *pdev; + +static int __init sensors_smf_init(void) +{ + int err; + unsigned short address; + struct resource res; + struct smf_sio_data sio_data; + + /* + * initialize sio_data->kind and sio_data->sioreg. + * when Super-I/O functions move to a separate file, the Super-I/O + * driver will probe and auto-detect the presence of a + * smf hardware monitor, and call probe() + */ + + if (smf_find(SMF_REG_ADDR, &address, &sio_data)) + return -ENODEV; + + err = platform_driver_register(&smf_driver); + if (err) + goto exit; + + pdev = platform_device_alloc(SIO_DRVNAME, address); + if (!pdev) { + err = -ENOMEM; + pr_err("Device allocation failed\n"); + goto exit_unregister; + } + + err = platform_device_add_data(pdev, &sio_data, + sizeof(struct smf_sio_data)); + if (err) { + pr_err("Platform data allocation failed\n"); + goto exit_device_put; + } + + memset(&res, 0, sizeof(res)); + res.name = SIO_DRVNAME; + res.start = address + IOREGION_OFFSET; + res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; + res.flags = IORESOURCE_IO; + + err = acpi_check_resource_conflict(&res); + if (err) + goto exit_device_put; + + err = platform_device_add_resources(pdev, &res, 1); + if (err) { + pr_err("Device resource addition failed (%d)\n", err); + goto exit_device_put; + } + + /* platform_device_add calls probe() */ + err = platform_device_add(pdev); + if (err) { + pr_err("Device addition failed (%d)\n", err); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(pdev); +exit_unregister: + platform_driver_unregister(&smf_driver); +exit: + return err; +} + + +static void __exit sensors_smf_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&smf_driver); + + /*Remove sysfs dell_kobj*/ + kobject_put(dell_kobj); +} + + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SMF driver"); +MODULE_PARM_DESC(force_id, "Override the detected device ID"); +MODULE_AUTHOR("Per Fremrot "); +MODULE_AUTHOR("Paavaanan "); + +module_init(sensors_smf_init); +module_exit(sensors_smf_exit); diff --git a/platform/broadcom/sonic-platform-modules-dell/common/fstrim.service b/platform/broadcom/sonic-platform-modules-dell/common/fstrim.service new file mode 100644 index 000000000000..cf740d3af34d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/common/fstrim.service @@ -0,0 +1,6 @@ +[Unit] +Description=Discard unused blocks + +[Service] +Type=oneshot +ExecStart=/sbin/fstrim -av diff --git a/platform/broadcom/sonic-platform-modules-dell/common/fstrim.timer b/platform/broadcom/sonic-platform-modules-dell/common/fstrim.timer new file mode 100644 index 000000000000..c6142dd94826 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/common/fstrim.timer @@ -0,0 +1,12 @@ +[Unit] +Description=Discard unused blocks once a week +Documentation=man:fstrim + +[Timer] +OnCalendar=weekly +OnBootSec=10min +AccuracySec=1h +Persistent=true + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-dell/debian/changelog b/platform/broadcom/sonic-platform-modules-dell/debian/changelog new file mode 100644 index 000000000000..90fed6f6d847 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/debian/changelog @@ -0,0 +1,11 @@ +sonic-dell-platform-modules (1.1) unstable; urgency=low + + * Add support for Dell S6100 + + -- Dell Team Wed, 08 Feb 2017 15:57:40 -0800 + +sonic-dell-platform-modules (1.0) unstable; urgency=low + + * Initial release + + -- Dell Team Mon, 6 Feb 2017 10:10:10 -0800 diff --git a/platform/broadcom/sonic-platform-modules-dell/debian/compat b/platform/broadcom/sonic-platform-modules-dell/debian/compat new file mode 100644 index 000000000000..ec635144f600 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/debian/compat @@ -0,0 +1 @@ +9 diff --git a/platform/broadcom/sonic-platform-modules-dell/debian/control b/platform/broadcom/sonic-platform-modules-dell/debian/control new file mode 100644 index 000000000000..45f94e04b9eb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/debian/control @@ -0,0 +1,17 @@ +Source: sonic-dell-platform-modules +Section: main +Priority: extra +Maintainer: Dell Team +Build-Depends: debhelper (>= 8.0.0), bzip2 +Standards-Version: 3.9.3 + +Package: platform-modules-z9100 +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as fan, led, sfp + +Package: platform-modules-s6100 +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as fan, led, sfp + diff --git a/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.init b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.init new file mode 100755 index 000000000000..c798b24ef235 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.init @@ -0,0 +1,40 @@ +#!/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 S6100 board. +### END INIT INFO + +case "$1" in +start) + echo -n "Setting up board... " + + /usr/local/bin/iom_power_on.sh + /usr/local/bin/s6100_platform.sh init + + echo "done." + ;; + +stop) + /usr/local/bin/s6100_platform.sh deinit + echo "done." + + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/platform-modules-s6100.init {start|stop}" + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.install b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.install new file mode 100644 index 000000000000..f456acb9d812 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.install @@ -0,0 +1,9 @@ +s6100/scripts/io_rd_wr.py usr/local/bin +s6100/scripts/iom_power_*.sh usr/local/bin +s6100/scripts/s6100_platform.sh usr/local/bin +common/dell_i2c_utils.sh usr/local/bin +common/fstrim.timer etc/systemd/system +common/fstrim.service etc/systemd/system +s6100/scripts/platform_sensors.py usr/local/bin +s6100/scripts/sensors usr/bin + diff --git a/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.postinst b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.postinst new file mode 100644 index 000000000000..aeabe1abd27c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-s6100.postinst @@ -0,0 +1,7 @@ +# postinst script for S6100 + +# Enable fstrim +systemctl enable fstrim.timer +systemctl start fstrim.timer + +#DEBHELPER# diff --git a/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-z9100.init b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-z9100.init new file mode 100755 index 000000000000..aefecf1d8158 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-z9100.init @@ -0,0 +1,40 @@ +#!/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 Z9100 board. +### END INIT INFO + +case "$1" in +start) + echo -n "Setting up board... " + + # /usr/local/bin/iom_power_on.sh + /usr/local/bin/z9100_platform.sh init + + echo "done." + ;; + +stop) + /usr/local/bin/z9100_platform.sh deinit + echo "done." + + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/platform-modules-z9100.init {start|stop}" + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-z9100.install b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-z9100.install new file mode 100644 index 000000000000..66d24e98e89c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-z9100.install @@ -0,0 +1,8 @@ +z9100/scripts/check_qsfp.sh usr/local/bin +z9100/scripts/z9100_platform.sh usr/local/bin +common/dell_i2c_utils.sh usr/local/bin +common/fstrim.timer etc/systemd/system +common/fstrim.service etc/systemd/system +z9100/scripts/platform_sensors.py usr/local/bin +z9100/scripts/sensors usr/bin +z9100/cfg/z9100-modules.conf etc/modules-load.d diff --git a/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-z9100.postinst b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-z9100.postinst new file mode 100644 index 000000000000..b11ae1f274c4 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/debian/platform-modules-z9100.postinst @@ -0,0 +1,7 @@ +# postinst script for Z9100 + +# Enable fstrim +systemctl enable fstrim.timer +systemctl start fstrim.timer + +#DEBHELPER# diff --git a/platform/broadcom/sonic-platform-modules-dell/debian/rules b/platform/broadcom/sonic-platform-modules-dell/debian/rules new file mode 100755 index 000000000000..12c3dffb1d4b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/debian/rules @@ -0,0 +1,45 @@ +#!/usr/bin/make -f + +export INSTALL_MOD_DIR:=extra + +KVERSION ?= $(shell uname -r) +KERNEL_SRC := /lib/modules/$(KVERSION) +MOD_SRC_DIR:= $(shell pwd) +MODULE_DIRS:= s6100 z9100 +COMMON_DIR := common + +%: + dh $@ + +override_dh_auto_build: + (for mod in $(MODULE_DIRS); do \ + if [ $$mod = "s6100" ]; then \ + cp $(COMMON_DIR)/*.c $(MOD_SRC_DIR)/$${mod}/modules/dell_s6100_lpc.c; \ + else \ + cp $(COMMON_DIR)/*.c $(MOD_SRC_DIR)/$${mod}/modules/dell_mailbox.c; \ + fi; \ + echo "making man page alias $$mod -> $$mod APIs";\ + make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + done) + +override_dh_auto_install: + (for mod in $(MODULE_DIRS); do \ + dh_installdirs -pplatform-modules-$${mod} \ + $(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$${mod}/modules/*.ko \ + debian/platform-modules-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + done) + +override_dh_usrlocal: + +override_dh_clean: + dh_clean + (for mod in $(MODULE_DIRS); do \ + if [ $$mod = "s6100" ]; then \ + rm -f $(MOD_SRC_DIR)/$${mod}/modules/dell_s6100_lpc.c; \ + else \ + rm -f $(MOD_SRC_DIR)/$${mod}/modules/dell_mailbox.c; \ + fi; \ + make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules clean; \ + done) + diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/modules/Makefile b/platform/broadcom/sonic-platform-modules-dell/s6100/modules/Makefile new file mode 100644 index 000000000000..0cdfbf5e67b8 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/modules/Makefile @@ -0,0 +1,2 @@ +obj-m := dell_s6100_iom_cpld.o dell_s6100_lpc.o + diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/modules/dell_s6100_iom_cpld.c b/platform/broadcom/sonic-platform-modules-dell/s6100/modules/dell_s6100_iom_cpld.c new file mode 100644 index 000000000000..6cc28f707a1f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/modules/dell_s6100_iom_cpld.c @@ -0,0 +1,266 @@ + +#include // included for all kernel modules +#include // included for KERN_INFO +#include // included for __init and __exit macros +#include +#include +#include +#include + + +//iom cpld slave address +#define IOM_CPLD_SLAVE_ADD 0x3e + +//iom cpld ver register +#define IOM_CPLD_SLAVE_VER 0x00 + +//qsfp reset cntrl reg on each iom +#define QSFP_RST_CRTL_REG0 0x10 +#define QSFP_RST_CRTL_REG1 0x11 + +//qsfp lp mode reg on each iom +#define QSFP_LPMODE_REG0 0x12 +#define QSFP_LPMODE_REG1 0x13 + +//qsfp mod presence reg on each iom +#define QSFP_MOD_PRS_REG0 0x16 +#define QSFP_MOD_PRS_REG1 0x17 + + +struct cpld_data { + struct i2c_client *client; + struct mutex update_lock; +}; + + +static void dell_s6100_iom_cpld_add_client(struct i2c_client *client) +{ + struct cpld_data *data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); + + if (!data) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); +} + +static void dell_s6100_iom_cpld_remove_client(struct i2c_client *client) +{ + struct cpld_data *data = i2c_get_clientdata(client); + kfree(data); + return; +} + +int dell_s6100_iom_cpld_read(struct cpld_data *data,unsigned short cpld_addr, u8 reg) +{ + int ret = -EPERM; + u8 high_reg =0x00; + + mutex_lock(&data->update_lock); + ret = i2c_smbus_write_byte_data(data->client, high_reg,reg); + ret = i2c_smbus_read_byte(data->client); + mutex_unlock(&data->update_lock); + + return ret; +} + +int dell_s6100_iom_cpld_write(struct cpld_data *data,unsigned short cpld_addr, u8 reg, u8 value) +{ + int ret = -EIO; + u16 devdata=0; + u8 high_reg =0x00; + + mutex_lock(&data->update_lock); + devdata = (value << 8) | reg; + i2c_smbus_write_word_data(data->client,high_reg,devdata); + mutex_unlock(&data->update_lock); + + return ret; +} +static ssize_t get_cpldver(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u8 devdata=0; + struct cpld_data *data = dev_get_drvdata(dev); + + ret = dell_s6100_iom_cpld_read(data,IOM_CPLD_SLAVE_ADD,IOM_CPLD_SLAVE_VER); + if(ret < 0) + return sprintf(buf, "read error"); + devdata = (u8)ret & 0xff; + return sprintf(buf,"IOM CPLD Version:0x%02x\n",devdata); +} + +static ssize_t get_modprs(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u16 devdata=0; + struct cpld_data *data = dev_get_drvdata(dev); + + ret = dell_s6100_iom_cpld_read(data,IOM_CPLD_SLAVE_ADD,QSFP_MOD_PRS_REG0); + if(ret < 0) + return sprintf(buf, "read error"); + devdata = (u16)ret & 0xff; + + ret = dell_s6100_iom_cpld_read(data,IOM_CPLD_SLAVE_ADD,QSFP_MOD_PRS_REG1); + if(ret < 0) + return sprintf(buf, "read error"); + devdata |= (u16)(ret & 0xff) << 8; + + return sprintf(buf,"0x%04x\n",devdata); +} + +static ssize_t get_lpmode(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u16 devdata=0; + struct cpld_data *data = dev_get_drvdata(dev); + + ret = dell_s6100_iom_cpld_read(data,IOM_CPLD_SLAVE_ADD,QSFP_LPMODE_REG0); + if(ret < 0) + return sprintf(buf, "read error"); + devdata = (u16)ret & 0xff; + + ret = dell_s6100_iom_cpld_read(data,IOM_CPLD_SLAVE_ADD,QSFP_LPMODE_REG1); + if(ret < 0) + return sprintf(buf, "read error"); + devdata |= (u16)(ret & 0xff) << 8; + + return sprintf(buf,"0x%04x\n",devdata); +} + +static ssize_t get_reset(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u16 devdata=0; + struct cpld_data *data = dev_get_drvdata(dev); + + ret = dell_s6100_iom_cpld_read(data,IOM_CPLD_SLAVE_ADD,QSFP_RST_CRTL_REG0); + if(ret < 0) + return sprintf(buf, "read error"); + devdata = (u16)ret & 0xff; + + ret = dell_s6100_iom_cpld_read(data,IOM_CPLD_SLAVE_ADD,QSFP_RST_CRTL_REG1); + if(ret < 0) + return sprintf(buf, "read error"); + devdata |= (u16)(ret & 0xff) << 8; + + return sprintf(buf,"0x%04x\n",devdata); +} + +static ssize_t set_lpmode(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct cpld_data *data = dev_get_drvdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + dell_s6100_iom_cpld_write(data,IOM_CPLD_SLAVE_ADD,QSFP_LPMODE_REG0,(u8)(devdata & 0xff)); + dell_s6100_iom_cpld_write(data,IOM_CPLD_SLAVE_ADD,QSFP_LPMODE_REG1,(u8)((devdata >> 8) & 0xff)); + + return count; +} + +static ssize_t set_reset(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct cpld_data *data = dev_get_drvdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + dell_s6100_iom_cpld_write(data,IOM_CPLD_SLAVE_ADD,QSFP_RST_CRTL_REG0,(u8)(devdata & 0xff)); + dell_s6100_iom_cpld_write(data,IOM_CPLD_SLAVE_ADD,QSFP_RST_CRTL_REG1,(u8)((devdata >> 8) & 0xff)); + + return count; +} + +static DEVICE_ATTR(iom_cpld_vers,S_IRUGO,get_cpldver, NULL); +static DEVICE_ATTR(qsfp_modprs, S_IRUGO,get_modprs, NULL); +static DEVICE_ATTR(qsfp_lpmode, S_IRUGO | S_IWUSR,get_lpmode,set_lpmode); +static DEVICE_ATTR(qsfp_reset, S_IRUGO | S_IWUSR,get_reset, set_reset); + +static struct attribute *i2c_cpld_attrs[] = { + &dev_attr_qsfp_lpmode.attr, + &dev_attr_qsfp_reset.attr, + &dev_attr_qsfp_modprs.attr, + &dev_attr_iom_cpld_vers.attr, + NULL, +}; + +static struct attribute_group i2c_cpld_attr_grp = { + .attrs = i2c_cpld_attrs, +}; + +static int dell_s6100_iom_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_dbg(&client->dev, "i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + dev_info(&client->dev, "chip found\n"); + dell_s6100_iom_cpld_add_client(client); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &i2c_cpld_attr_grp); + if (status) { + printk(KERN_INFO "Cannot create sysfs\n"); + } + return 0; + +exit: + return status; +} + +static int dell_s6100_iom_cpld_remove(struct i2c_client *client) +{ + dell_s6100_iom_cpld_remove_client(client); + return 0; +} + + +static const struct i2c_device_id dell_s6100_iom_cpld_id[] = { + { "dell_s6100_iom_cpld", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, dell_s6100_iom_cpld_id); + +static struct i2c_driver dell_s6100_iom_cpld_driver = { + .driver = { + .name = "dell_s6100_iom_cpld", + }, + .probe = dell_s6100_iom_cpld_probe, + .remove = dell_s6100_iom_cpld_remove, + .id_table = dell_s6100_iom_cpld_id, +}; + + + +static int __init dell_s6100_iom_cpld_init(void) +{ + return i2c_add_driver(&dell_s6100_iom_cpld_driver); +} + +static void __exit dell_s6100_iom_cpld_exit(void) +{ + i2c_del_driver(&dell_s6100_iom_cpld_driver); +} + +MODULE_AUTHOR("Srideep Devireddy "); +MODULE_DESCRIPTION("dell_s6100_iom_cpld driver"); +MODULE_LICENSE("GPL"); + +module_init(dell_s6100_iom_cpld_init); +module_exit(dell_s6100_iom_cpld_exit); diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/README b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/README new file mode 100644 index 000000000000..b141bb05e2e4 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/README @@ -0,0 +1,146 @@ +Dell S6100 64x40G support +------------------------ + +summary:- +S6100 has 64 x 40G port capacity +S6100 supports 4 IO Modules which are pluggable. Below section covers how to power on IO modules +and initialize the device drivers and configure necessary mux to send traffic. + + +Necessary drivers and scripts to package + +1) Users are expected to build necessary drivers for kernel and package to acess it on the target + + a) Mgmt phy driver + This supports BCM 54616 phy driver ( enable CONFIG_IGB in kernel config) + b) driver-support-sff-8436-eeprom.patch + c) driver-support-sff-8436-eeprom-update.patch + This driver supports QSFP EEPROM + d) dell_s6100_iom_cpld.ko + This driver provides support for cpld on 4 x 40Gig-IOM modules + e) i2c_mux_pca954x.ko + This driver provides support for i2c mux/switch (pca954x) + f) dell_s6100_lpc.ko + This driver porivide support for reading all the platform info from SMF. + + +2) Users are also expected to package below scripts for platform initialization and i2c tree creation + + a) iom_power.sh + This script is used to power on IO modules on S6100 + b) io_rd_wr.py +This script is generic LPC/io read/write utility (can also access system cpld) + +steps to platform initialization (Tested with 3.16) + +1) After power up of S6100 + + a) power up the io modules by executing (./iom_power.sh) + b) insert i2c_mux_pca954x.ko,dell_s6100_iom_cpld.ko,dell_s6100_lpc.ko + + Above 2 steps can be called by including them in below 2 step + +2) Build i2c device tree/device initialization by calling below script + + ./s6100_platform_init.sh + +3) IOM cpld devices are created under "/sys/bus/i2c/drivers/dell_s6100_iom_cpld/" + QSFP handles via sysfs i2c bus #( 15,16,17,18-003e) + iom_cpld_vers -- Displays CPLD version(RO) + qsfp_lpmode -- lpmode settings(RW) + qsfp_modprs -- modules presence (RO) + qsfp_reset -- reset settings (RW) + + ls /sys/bus/i2c/drivers/dell_s6100_iom_cpld/15-003e/ + driver/ iom_cpld_vers modalias name power/ qsfp_lpmode qsfp_modprs qsfp_reset subsystem/ uevent + +4) step 2 script also builds/attach sfp device tress in sysfs + + Example for reading EEPROM data + + cat /sys/bus/i2c/devices/19-0050/eeprom | hexdump -C + 00000000 0d 00 00 0f 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000010 00 00 00 00 00 00 18 1b 00 00 82 20 00 00 00 00 |........... ....| + 00000020 00 00 00 00 00 00 00 00 00 00 16 4d 16 4d 16 4d |...........M.M.M| + 00000030 16 4d 34 bf 44 bf 54 bf 64 bf 00 00 00 00 00 00 |.M4.D.T.d.......| + 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + * + 00000070 00 00 00 00 00 00 00 ff ff ff ff ff ff ff ff 00 |................| + 00000080 0d 00 0c 04 00 00 00 40 40 02 d5 05 69 00 00 32 |.......@@...i..2| + 00000090 00 00 00 00 46 49 4e 49 53 41 52 20 43 4f 52 50 |....FINISAR CORP| + 000000a0 20 20 20 20 07 00 90 65 46 54 4c 34 31 30 51 45 | ...eFTL410QE| + 000000b0 31 43 20 20 20 20 20 20 41 33 42 68 07 d0 46 70 |1C A3Bh..Fp| + 000000c0 00 01 04 d0 4d 4c 54 30 30 51 33 20 20 20 20 20 |....MLT00Q3 | + 000000d0 20 20 20 20 31 32 30 31 31 35 20 20 08 00 00 38 | 120115 ...8| + 000000e0 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | | + 000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + * + 00000200 4b 00 fb 00 46 00 00 00 00 00 00 00 00 00 00 00 |K...F...........| + 00000210 94 70 6e f0 86 c4 7b 0c 00 00 00 00 00 00 00 00 |.pn...{.........| + 00000220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + * + 00000260 00 00 11 11 00 00 00 00 00 00 00 00 00 00 22 22 |..............""| + 00000270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000280 + +5)SMF driver details/Readme are as below + + S6100 SMF + --------- + + S6100 has one SMF soc on CPU board. This is via LPC interface, provide access to all the below functionality + + a) Read sensors details on the system + b) Read PSU details on the system + c) Read fans/Fan tray details on the system + d) Read/Write to system led + + dell_s6100_lpc create entry for all the above necessary platform components as attributes in below path. + + /sys/devices/platform/dell_s6100_lpc/ + + + LED:- + ---- + + Get /set operations on sys_led + + Get operation + cat /sys/devices/platform/dell_s6100_lpc/sys_led + 0x80 + + Set operation + echo 83 > sys/devices/platform/dell_s6100_lpc/sys_led + + Sensors:- + -------- + + Max no of sensors on the system ( 11 sensors are available on fully loaded system) + + cat /sys/devices/platform/dell_s6100_lpc/max_num_temp_sensors + b + + cat /sys/devices/platform/dell_s6100_lpc/temp_sensor_1 ( temp_sensor_1 <85>.temp_sensor_11) + + Fan tray speed:- + -------------- + + /sys/devices/platform/dell_s6100_lpc/fan_tray_1_speed (fan_tray_1_speed - fan_tray_4_speed) + 13700 + + cat /sys/devices/platform/dell_s6100_lpc/psu1_max_pwr + 110 + cat /sys/devices/platform/dell_s6100_lpc/psu2_max_pwr + 110 + + cat /sys/devices/platform/dell_s6100_lpc/psu_total_pwr + 232 + + + + + + + + + diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/io_rd_wr.py b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/io_rd_wr.py new file mode 100755 index 000000000000..dc9dd09807c2 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/io_rd_wr.py @@ -0,0 +1,93 @@ +#!/usr/bin/python +#Script to read/write the io based registers + +import sys +import os +import getopt +import struct + +io_resource='/dev/port' + +def usage(): + ''' This is the Usage Method ''' + + print 'Utility for IO read/write' + print '\t\t io_rd_wr.py --get --offset ' + print '\t\t io_rd_wr.py --set --val --offset ' + sys.exit(1) + +def io_reg_read(io_resource,offset): + fd=os.open(io_resource, os.O_RDONLY) + if(fd<0): + print 'file open failed %s"%io_resource' + return + if(os.lseek(fd, offset, os.SEEK_SET) != offset): + print 'lseek failed on %s'%io_resource + return + buf=os.read(fd,1) + reg_val1=ord(buf) + print 'reg value %x'%reg_val1 + os.close(fd) + +def io_reg_write(io_resource,offset,val): + fd=os.open(io_resource,os.O_RDWR) + if(fd<0): + print 'file open failed %s"%io_resource' + return + if(os.lseek(fd, offset, os.SEEK_SET) != offset): + print 'lseek failed on %s'%io_resource + return + ret=os.write(fd,struct.pack('B',val)) + if(ret != 1): + print 'write failed %d'%ret + return + os.close(fd) + +def main(argv): + + ''' The main function will read the user input from the + command line argument and process the request ''' + + opts = '' + val = '' + choice = '' + resouce = '' + offset = '' + + try: + opts, args = getopt.getopt(argv, "hgs:" , \ + ["val=","offset=","help", "get", "set"]) + + except getopt.GetoptError: + usage() + + for opt,arg in opts: + + if opt in ('-h','--help'): + choice = 'help' + + elif opt in ('-g', '--get'): + choice = 'get' + + elif opt in ('-s', '--set'): + choice = 'set' + + elif opt == '--offset': + offset = int(arg,16) + + elif opt == '--val': + val = int(arg,16) + + if choice == 'get' and offset != '': + io_reg_read(io_resource,offset) + + elif choice == 'set' and offset != '' and val != '': + io_reg_write(io_resource,offset,val) + + else: + usage() + +#Calling the main method +if __name__ == "__main__": + main(sys.argv[1:]) + diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/iom_power_off.sh b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/iom_power_off.sh new file mode 100755 index 000000000000..a7671cfe589c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/iom_power_off.sh @@ -0,0 +1,16 @@ +#!/bin/bash +#This script is used to power off IO modules +# IOM can be controlled via SMF using mailbox registers +# write 0x2 to 0x04D9 to power off IOM 1 +./io_rd_wr.py --set --val 0x04 --offset 0x210 +./io_rd_wr.py --set --val 0xd9 --offset 0x211 +./io_rd_wr.py --set --val 0x2 --offset 0x213 +# write 0x2 to 0x04DA to power off IOM 2 +./io_rd_wr.py --set --val 0xda --offset 0x211 +./io_rd_wr.py --set --val 0x2 --offset 0x213 +# write 0x2 to 0x04DB to power off IOM 3 +./io_rd_wr.py --set --val 0xdb --offset 0x211 +./io_rd_wr.py --set --val 0x2 --offset 0x213 +# write 0x2 to 0x04DC to power off IOM 4 +./io_rd_wr.py --set --val 0xdc --offset 0x211 +./io_rd_wr.py --set --val 0x2 --offset 0x213 diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/iom_power_on.sh b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/iom_power_on.sh new file mode 100755 index 000000000000..3d31170803e1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/iom_power_on.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# This script is used to power on IO modules +# IOM can be controlled via SMF using mailbox registers + +# write 0x1 to 0x04D9 to power up IOM 1 +/usr/local/bin/io_rd_wr.py --set --val 0x04 --offset 0x210 +/usr/local/bin/io_rd_wr.py --set --val 0xd9 --offset 0x211 +/usr/local/bin/io_rd_wr.py --set --val 0x1 --offset 0x213 +# write 0x1 to 0x04DA to power up IOM 2 +/usr/local/bin/io_rd_wr.py --set --val 0xda --offset 0x211 +/usr/local/bin/io_rd_wr.py --set --val 0x1 --offset 0x213 +# write 0x1 to 0x04DB to power up IOM 3 +/usr/local/bin/io_rd_wr.py --set --val 0xdb --offset 0x211 +/usr/local/bin/io_rd_wr.py --set --val 0x1 --offset 0x213 +# write 0x1 to 0x04DC to power up IOM 4 +/usr/local/bin/io_rd_wr.py --set --val 0xdc --offset 0x211 +/usr/local/bin/io_rd_wr.py --set --val 0x1 --offset 0x213 diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/platform_sensors.py b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/platform_sensors.py new file mode 100755 index 000000000000..fb59df1ee7f3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/platform_sensors.py @@ -0,0 +1,322 @@ +#!/usr/bin/python + +# On S6100, the Platform Management Controller runs the +# thermal algorithm. It provides a mailbox for the Host +# to query relevant thermals. The dell_mailbox module +# provides the sysfs support for the following objects: +# * onboard temperature sensors +# * FAN trays +# * PSU +# +import os +import sys +import logging + +S6100_MAX_FAN_TRAYS = 4 +S6100_MAX_PSUS = 2 +S6100_MAX_IOMS = 4 + +MAILBOX_DIR = "/sys/devices/platform/SMF.512/hwmon/hwmon1" +iom_status_list = [] + +# Get a mailbox register + + +def get_pmc_register(reg_name): + retval = 'ERR' + mb_reg_file = MAILBOX_DIR+'/'+reg_name + + if (not os.path.isfile(mb_reg_file)): + print mb_reg_file, 'not found !' + return retval + + try: + with open(mb_reg_file, 'r') as fd: + retval = fd.read() + except Exception as error: + logging.error("Unable to open ", mb_reg_file, "file !") + + retval = retval.rstrip('\r\n') + retval = retval.lstrip(" ") + return retval + +logging.basicConfig(level=logging.DEBUG) + +if (os.path.isdir(MAILBOX_DIR)): + print 'dell-s6100-lpc' + print 'Adapter: S6100 Platform Management Controller' +else: + logging.error('S6100 Platform Management Controller module not loaded !') + # sys.exit(0) + +# Print the information for temperature sensors + + +def print_temperature_sensors(): + print("\nOnboard Temperature Sensors:") + print ' CPU: ',\ + int(get_pmc_register('temp1_input'))/1000, 'C' + print ' BCM56960 (PSU side): ',\ + int(get_pmc_register('temp2_input'))/1000, 'C' + print ' System Outlet 1 (switch board): ',\ + int(get_pmc_register('temp3_input'))/1000, 'C' + print ' BCM56960 (IO side): ',\ + int(get_pmc_register('temp4_input'))/1000, 'C' + print ' System Outlet 2 (CPU board): ',\ + int(get_pmc_register('temp9_input'))/1000, 'C' + print ' System Inlet Left (IO side): ',\ + int(get_pmc_register('temp10_input'))/1000, 'C' + print ' System Inlet Right (IO side): ',\ + int(get_pmc_register('temp11_input'))/1000, 'C' + + iom_status = get_pmc_register('iom_presence') + iom_status = int(iom_status, 16) + + iom_presence = get_pmc_register('iom_status') + + if (iom_presence != 'ERR'): + iom_presence = int(iom_presence, 16) + + # IOM presence : 0 => IOM present + # Temperature sensors 5..8 correspond to IOM 1..4 + for iom in range(0, S6100_MAX_IOMS): + if (~iom_presence & (1 << iom)): + iom_sensor_indx = iom + 5 + print ' IOM ' + str(iom + 1) + ':\t\t\t ',\ + int(get_pmc_register('temp'+str(iom_sensor_indx) + + '_input'))/1000, 'C' + + # Save the IOM Status for later use + if (~iom_status & (1 << iom)): + iom_status_list.append('ON') + else: + iom_status_list.append('OFF') + else: + iom_status_list.append('Not present') + print ' IOM ' + str(iom + 1) + ':\t\t\t ', 'Not present' + else: + logging.error('Unable to check IOM presence') + +print_temperature_sensors() + + +# Print the information for voltage sensors + + +def print_voltage_sensors(): + print("\nOnboard Voltage Sensors:") + + print ' CPU XP3R3V_EARLY ',\ + float(get_pmc_register('in1_input'))/1000, 'V' + print ' CPU XP5R0V_CP ',\ + float(get_pmc_register('in2_input'))/1000, 'V' + print ' CPU XP3R3V_STD ',\ + float(get_pmc_register('in3_input'))/1000, 'V' + print ' CPU XP3R3V_CP ',\ + float(get_pmc_register('in4_input'))/1000, 'V' + print ' CPU XP0R75V_VTT_A ',\ + float(get_pmc_register('in5_input'))/1000, 'V' + print ' CPU XPPR75V_VTT_B ',\ + float(get_pmc_register('in6_input'))/1000, 'V' + print ' CPU XP1R07V_CPU ',\ + float(get_pmc_register('in7_input'))/1000, 'V' + print ' CPU XP1R0V_CPU ',\ + float(get_pmc_register('in8_input'))/1000, 'V' + print ' CPU XP12R0V ',\ + float(get_pmc_register('in9_input'))/1000, 'V' + print ' CPU VDDR_CPU_2 ',\ + float(get_pmc_register('in10_input'))/1000, 'V' + print ' CPU VDDR_CPU_1 ',\ + float(get_pmc_register('in11_input'))/1000, 'V' + print ' CPU XP1R5V_CLK ',\ + float(get_pmc_register('in12_input'))/1000, 'V' + print ' CPU XP1R8V_CPU ',\ + float(get_pmc_register('in13_input'))/1000, 'V' + print ' CPU XP1R0V_CPU_VNN ',\ + float(get_pmc_register('in14_input'))/1000, 'V' + print ' CPU XP1R0V_CPU_VCC ',\ + float(get_pmc_register('in15_input'))/1000, 'V' + print ' CPU XP1R5V_EARLY ',\ + float(get_pmc_register('in16_input'))/1000, 'V' + print ' SW XP3R3V_MON ',\ + float(get_pmc_register('in17_input'))/1000, 'V' + print ' SW XP1R25V_MON ',\ + float(get_pmc_register('in19_input'))/1000, 'V' + print ' SW XP1R2V_MON ',\ + float(get_pmc_register('in20_input'))/1000, 'V' + print ' SW XP1R0V_SW_MON ',\ + float(get_pmc_register('in21_input'))/1000, 'V' + print ' SW XP1R0V_ROV_SW_MON ',\ + float(get_pmc_register('in22_input'))/1000, 'V' + print ' SW XR1R0V_BCM84752_MON ',\ + float(get_pmc_register('in23_input'))/1000, 'V' + print ' SW XP5V_MB_MON ',\ + float(get_pmc_register('in24_input'))/1000, 'V' + print ' SW XP3R3V_FPGA_MON ',\ + float(get_pmc_register('in26_input'))/1000, 'V' + print ' SW XP3R3V_EARLY_MON ',\ + float(get_pmc_register('in27_input'))/1000, 'V' + +print_voltage_sensors() + +# Print the information for 1 Fan Tray + + +def print_fan_tray(tray): + + Fan_Status = ['Normal', 'Abnormal'] + Airflow_Direction = ['B2F', 'F2B'] + + print ' Fan Tray ' + str(tray) + ':' + + if (tray == 1): + fan1_speed = get_pmc_register('fan1_input') + air_flow_reg = int(get_pmc_register('fan1_airflow'), 16) + fan1_status = 0 if fan1_speed >= 1000 else 1 + + print ' Fan Speed: ', fan1_speed, 'RPM' + print ' Fan State: ', Fan_Status[fan1_status] + print ' Air Flow: ', Airflow_Direction[air_flow_reg] + elif (tray == 2): + fan1_speed = get_pmc_register('fan3_input') + air_flow_reg = int(get_pmc_register('fan3_airflow'), 16) + fan1_status = 0 if fan1_speed >= 1000 else 1 + + print ' Fan Speed: ', fan1_speed, 'RPM' + print ' Fan State: ', Fan_Status[fan1_status] + print ' Air Flow: ', Airflow_Direction[air_flow_reg] + elif (tray == 3): + fan1_speed = get_pmc_register('fan5_input') + air_flow_reg = int(get_pmc_register('fan5_airflow'), 16) + fan1_status = 0 if fan1_speed >= 1000 else 1 + + print ' Fan Speed: ', fan1_speed, 'RPM' + print ' Fan State: ', Fan_Status[fan1_status] + print ' Air Flow: ', Airflow_Direction[air_flow_reg] + elif (tray == 4): + fan1_speed = get_pmc_register('fan7_input') + air_flow_reg = int(get_pmc_register('fan7_airflow'), 16) + fan1_status = 0 if fan1_speed >= 1000 else 1 + + print ' Fan Speed: ', fan1_speed, 'RPM' + print ' Fan State: ', Fan_Status[fan1_status] + print ' Air Flow: ', Airflow_Direction[air_flow_reg] + +print('\nFan Trays:') +fan_tray_presence = get_pmc_register('fan_tray_presence') + +if (fan_tray_presence != 'ERR'): + fan_tray_presence = int(fan_tray_presence, 16) + + for tray in range(0, S6100_MAX_FAN_TRAYS): + if (fan_tray_presence & (1 << tray)): + print_fan_tray(tray + 1) + else: + print '\n Fan Tray ' + str(tray + 1) + ': Not present' +else: + logging.error('Unable to read FAN presence') + +# Print the information for PSU1, PSU2 + + +def print_psu(psu): + Psu_Type = ['Mismatch', 'Normal'] + Psu_Input_Type = ['AC', 'DC'] + PSU_STATUS_TYPE_BIT = 4 + PSU_STATUS_INPUT_TYPE_BIT = 1 + PSU_FAN_PRESENT_BIT = 2 + PSU_FAN_STATUS_BIT = 1 + PSU_FAN_AIR_FLOW_BIT = 0 + Psu_Fan_Presence = ['Present', 'Absent'] + Psu_Fan_Status = ['Normal', 'Abnormal'] + Psu_Fan_Airflow = ['B2F', 'F2B'] + + print ' PSU ' + str(psu) + ':' + if (psu == 1): + psu_status = int(get_pmc_register('psu1_presence'), 16) + else: + psu_status = int(get_pmc_register('psu2_presence'), 16) + + psu_input_type = (psu_status & (1 << PSU_STATUS_INPUT_TYPE_BIT)) >>\ + PSU_STATUS_INPUT_TYPE_BIT + psu_type = (psu_status & (1 << PSU_STATUS_TYPE_BIT)) >>\ + PSU_STATUS_TYPE_BIT + + print ' Input: ', Psu_Input_Type[psu_input_type] + print ' Type: ', Psu_Type[psu_type] + + # PSU FAN details + if (psu == 1): + print ' FAN Speed: ', get_pmc_register('fan11_input'), 'RPM' + psu_fan_airflow = int(get_pmc_register('fan11_airflow')) + psu_fan_status = int(get_pmc_register('fan11_alarm')) + psu_fan_present = int(get_pmc_register('fan11_fault')) + input_voltage = float(get_pmc_register('in29_input')) / 1000 + output_voltage = float(get_pmc_register('in30_input')) / 1000 + input_current = float(get_pmc_register('curr601_input')) / 100 + output_current = float(get_pmc_register('curr602_input')) / 100 + input_power = float(get_pmc_register('power1_input')) / 1000000 + output_power = float(get_pmc_register('power2_input')) / 1000000 + if (input_power != 0): + psu_fan_temp = int(get_pmc_register('temp14_input'))/1000 + else: + print ' FAN Speed: ', get_pmc_register('fan12_input'), 'RPM' + psu_fan_airflow = int(get_pmc_register('fan12_airflow')) + psu_fan_status = int(get_pmc_register('fan12_alarm')) + psu_fan_present = int(get_pmc_register('fan12_fault')) + input_voltage = float(get_pmc_register('in31_input')) / 1000 + output_voltage = float(get_pmc_register('in32_input')) / 1000 + input_current = float(get_pmc_register('curr701_input')) / 100 + output_current = float(get_pmc_register('curr702_input')) / 100 + input_power = float(get_pmc_register('power3_input')) / 1000000 + output_power = float(get_pmc_register('power4_input')) / 1000000 + if (input_power != 0): + psu_fan_temp = int(get_pmc_register('temp15_input'))/1000 + print ' FAN: ', Psu_Fan_Presence[psu_fan_present] + print ' FAN Status: ', Psu_Fan_Status[psu_fan_status] + print ' FAN AIRFLOW: ', Psu_Fan_Airflow[psu_fan_airflow] + + # PSU input & output monitors + print ' Input Voltage: %6.2f' % (input_voltage), 'V' + + print ' Output Voltage: %6.2f' % (output_voltage), 'V' + + print ' Input Current: %6.2f' % (input_current), 'A' + + print ' Output Current: %6.2f' % (output_current), 'A' + + print ' Input Power: %6.2f' % (input_power), 'W' + + print ' Output Power: %6.2f' % (output_power), 'W' + + # PSU firmware gives spurious temperature reading without input power + if (input_power != 0): + print ' Temperature: ', psu_fan_temp, 'C' + else: + print ' Temperature: ', 'NA' + +print('\nPSUs:') +for psu in range(1, S6100_MAX_PSUS + 1): + + if (psu == 1): + psu_status = get_pmc_register('psu1_presence') + else: + psu_status = get_pmc_register('psu2_presence') + + if (psu_status != 'ERR'): + psu_status = int(psu_status, 16) + if (~psu_status & 0b1): + print_psu(psu) + else: + print '\n PSU ', psu, 'Not present' + else: + logging.error('Unable to check PSU presence') + +print '\n Total Power: ', get_pmc_register('current_total_power'), 'W' + +print('\nIO Modules:') + +for iom in range(1, S6100_MAX_IOMS+1): + print ' IOM ' + str(iom) + ' :' + iom_status_list[iom - 1] + +print '\n' diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/s6100_platform.sh b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/s6100_platform.sh new file mode 100755 index 000000000000..be143cb9efe7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/s6100_platform.sh @@ -0,0 +1,205 @@ +#!/bin/bash + +#platform init script for Dell S6100 + +source dell_i2c_utils.sh + +init_devnum() { + found=0 + for devnum in 0 1; do + devname=`cat /sys/bus/i2c/devices/i2c-${devnum}/name` + # iSMT adapter can be at either dffd0000 or dfff0000 + if [[ $devname == 'SMBus iSMT adapter at '* ]]; then + found=1 + break + fi + done + + [[ $found -eq 0 ]] && echo "cannot find iSMT" && exit 1 +} + +# Attach/Detach CPU board mux @ 0x70 +cpu_board_mux() { + case $1 in + "new_device") i2c_config "echo pca9547 0x70 > /sys/bus/i2c/devices/i2c-${devnum}/$1" + ;; + "delete_device") i2c_config "echo 0x70 > /sys/bus/i2c/devices/i2c-${devnum}/$1" + ;; + *) echo "s6100_platform: cpu_board_mux: invalid command !" + ;; + esac +} + +# Attach/Detach Switchboard MUX @ 0x71 +switch_board_mux() { + case $1 in + "new_device") i2c_config "echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-4/$1" + ;; + "delete_device") i2c_config "echo 0x71 > /sys/bus/i2c/devices/i2c-4/$1" + ;; + *) echo "s6100_platform: switch_board_mux : invalid command !" + ;; + esac +} + +# Attach/Detach syseeprom on CPU board +sys_eeprom() { + case $1 in + "new_device") i2c_config "echo 24c02 0x50 > /sys/bus/i2c/devices/i2c-2/$1" + ;; + "delete_device") i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-2/$1" + ;; + *) echo "s6100_platform: sys_eeprom : invalid command !" + ;; + esac +} + +#Attach/Detach CPLD devices to drivers for each IOM +switch_board_cpld() { + case $1 in + "new_device") + for ((i=14;i<=17;i++)); + do + i2c_config "echo dell_s6100_iom_cpld 0x3e > /sys/bus/i2c/devices/i2c-$i/$1" + done + ;; + "delete_device") + for ((i=14;i<=17;i++)); + do + i2c_config "echo 0x3e > /sys/bus/i2c/devices/i2c-$i/$1" + done + ;; + *) echo "s6100_platform: switch_board_cpld : invalid command !" + ;; + esac +} + +#Attach/Detach the MUX connecting all QSFPs on each IOM @0x71 and 0x72 +switch_board_qsfp_mux() { + case $1 in + "new_device") + for ((i=9;i>=6;i--)); + do + # 0x71 mux on the IOM 1 + mux_index=$(expr $i - 5) + echo "Attaching PCA9548 $mux_index" + i2c_config "echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-$i/$1" + i2c_config "echo pca9548 0x72 > /sys/bus/i2c/devices/i2c-$i/$1" + done + ;; + "delete_device") + for ((i=9;i>=6;i--)); + do + # 0x71 mux on the IOM 1 + mux_index=$(expr $i - 5) + echo "Detaching PCA9548 $mux_index" + i2c_config "echo 0x71 > /sys/bus/i2c/devices/i2c-$devnum/i2c-$i/$1" + i2c_config "echo 0x72 > /sys/bus/i2c/devices/i2c-$devnum/i2c-$i/$1" + done + ;; + *) echo "s6100_platform: switch_board_qsfp_mux: invalid command !" + ;; + esac +} + +#Attach/Detach the SFP modules on PCA9548_2 +switch_board_sfp() { + case $1 in + "new_device") i2c_config "echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-11/$1" + i2c_config "echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-12/$1" + ;; + "delete_device") i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-11/$1" + i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-12/$1" + ;; + *) echo "s6100_platform: switch_board_sfp: invalid command !" + ;; + esac +} + +#Add/Delete ($1) a range ($2..$3) of QSFPs +qsfp_device_mod() { + case $1 in + "new_device") for ((i=$2;i<=$3;i++)); + do + i2c_config "echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-$i/$1" + done + ;; + "delete_device") for ((i=$2;i<=$3;i++)); + do + i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-$i/$1" + done + ;; + *) echo "s6100_platform: qsfp_device_mod: invalid command $1:$2,$3!" + ;; + esac +} + +# Attach/Detach 16 instances of QSFP ports on each IO modules +# eeprom can dump data using below command +switch_board_qsfp() { + if i2c_poll_bus_exists "/sys/bus/i2c/devices/i2c-18"; then + qsfp_device_mod $1 18 33 + fi + + if i2c_poll_bus_exists "/sys/bus/i2c/devices/i2c-34"; then + qsfp_device_mod $1 34 49 + fi + + if i2c_poll_bus_exists "/sys/bus/i2c/devices/i2c-50"; then + qsfp_device_mod $1 50 65 + fi + + if i2c_poll_bus_exists "/sys/bus/i2c/devices/i2c-66"; then + qsfp_device_mod $1 66 81 + fi +} + +# Enable/Disable low power mode on all QSFP ports +switch_board_qsfp_lpmode() { + case $1 in + "enable") value=0xffff + ;; + "disable") value=0x0 + ;; + *) echo "s6100_platform: switch_board_qsfp_lpmode: invalid command $1!" + return + ;; + esac + echo $value > /sys/class/i2c-adapter/i2c-14/14-003e/qsfp_lpmode + echo $value > /sys/class/i2c-adapter/i2c-15/15-003e/qsfp_lpmode + echo $value > /sys/class/i2c-adapter/i2c-16/16-003e/qsfp_lpmode + echo $value > /sys/class/i2c-adapter/i2c-17/17-003e/qsfp_lpmode +} + +init_devnum + +if [[ "$1" == "init" ]]; then + modprobe i2c-dev + modprobe i2c-mux-pca954x force_deselect_on_exit=1 + modprobe dell_s6100_iom_cpld + modprobe dell_s6100_lpc + + cpu_board_mux "new_device" + switch_board_mux "new_device" + sys_eeprom "new_device" + switch_board_cpld "new_device" + switch_board_qsfp_mux "new_device" + switch_board_sfp "new_device" + switch_board_qsfp "new_device" + switch_board_qsfp_lpmode "disable" +elif [[ "$1" == "deinit" ]]; then + switch_board_sfp "delete_device" + switch_board_cpld "delete_device" + switch_board_mux "delete_device" + sys_eeprom "delete_device" + switch_board_qsfp "delete_device" + switch_board_qsfp_mux "delete_device" + cpu_board_mux "delete_device" + + modprobe -r dell_s6100_lpc + modprobe -r dell_s6100_iom_cpld + modprobe -r i2c-mux-pca954x + modprobe -r i2c-dev +else + echo "s6100_platform : Invalid option !" +fi diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/sensors b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/sensors new file mode 100755 index 000000000000..030d636a1565 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/scripts/sensors @@ -0,0 +1,7 @@ +#!/bin/bash +docker exec -i pmon sensors "$@" + +#To probe sensors not part of lm-sensors +if [ -r /usr/local/bin/platform_sensors.py ]; then + python /usr/local/bin/platform_sensors.py +fi diff --git a/platform/broadcom/sonic-platform-modules-dell/z9100/cfg/z9100-modules.conf b/platform/broadcom/sonic-platform-modules-dell/z9100/cfg/z9100-modules.conf new file mode 100644 index 000000000000..66f002a5fc94 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/z9100/cfg/z9100-modules.conf @@ -0,0 +1,15 @@ +# /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 + diff --git a/platform/broadcom/sonic-platform-modules-dell/z9100/modules/Makefile b/platform/broadcom/sonic-platform-modules-dell/z9100/modules/Makefile new file mode 100644 index 000000000000..46da5beec391 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/z9100/modules/Makefile @@ -0,0 +1,2 @@ +obj-m := dell_mailbox.o dell_z9100_cpld.o + diff --git a/platform/broadcom/sonic-platform-modules-dell/z9100/modules/dell_z9100_cpld.c b/platform/broadcom/sonic-platform-modules-dell/z9100/modules/dell_z9100_cpld.c new file mode 100644 index 000000000000..01c3133354b1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/z9100/modules/dell_z9100_cpld.c @@ -0,0 +1,273 @@ + + +/* Includes only 3 things. + * a. QSFP Reset control + * b. LP Mode + * c. Module Presence + */ +#include // included for all kernel modules +#include // included for KERN_INFO +#include // included for __init and __exit macros +#include +#include +#include +#include + + +//iom cpld slave address +#define IOM_CPLD_SLAVE_ADD 0x3e + +//iom cpld ver register +#define IOM_CPLD_SLAVE_VER 0x00 + +//qsfp reset cntrl reg on each iom +#define QSFP_RST_CRTL_REG0 0x10 +#define QSFP_RST_CRTL_REG1 0x11 + +//qsfp lp mode reg on each iom +#define QSFP_LPMODE_REG0 0x12 +#define QSFP_LPMODE_REG1 0x13 + +//qsfp mod presence reg on each iom +#define QSFP_MOD_PRS_REG0 0x16 +#define QSFP_MOD_PRS_REG1 0x17 + + +struct cpld_data { + struct i2c_client *client; + struct mutex update_lock; +}; + + +static void dell_z9100_iom_cpld_add_client(struct i2c_client *client) +{ + struct cpld_data *data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); + + if (!data) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); +} + +static void dell_z9100_iom_cpld_remove_client(struct i2c_client *client) +{ + struct cpld_data *data = i2c_get_clientdata(client); + kfree(data); + return ; +} + +int dell_z9100_iom_cpld_read(struct cpld_data *data, u8 reg) +{ + int ret = -EPERM; + u8 high_reg =0x00; + + mutex_lock(&data->update_lock); + + ret = i2c_smbus_write_byte_data(data->client, high_reg,reg); + ret = i2c_smbus_read_byte(data->client); + mutex_unlock(&data->update_lock); + + return ret; +} + +int dell_z9100_iom_cpld_write(struct cpld_data *data,u8 reg, u8 value) +{ + int ret = -EIO; + u16 devdata=0; + u8 high_reg =0x00; + + mutex_lock(&data->update_lock); + devdata = (value << 8) | reg; + i2c_smbus_write_word_data(data->client,high_reg,devdata); + mutex_unlock(&data->update_lock); + + return ret; +} +static ssize_t get_cpldver(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u8 devdata=0; + struct cpld_data *data = dev_get_drvdata(dev); + + ret = dell_z9100_iom_cpld_read(data,IOM_CPLD_SLAVE_VER); + if(ret < 0) + return sprintf(buf, "read error"); + devdata = (u8)ret & 0xff; + return sprintf(buf,"IOM CPLD Version:0x%02x\n",devdata); +} + +static ssize_t get_modprs(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u16 devdata=0; + struct cpld_data *data = dev_get_drvdata(dev); + + ret = dell_z9100_iom_cpld_read(data,QSFP_MOD_PRS_REG0); + if(ret < 0) + return sprintf(buf, "read error"); + devdata = (u16)ret & 0xff; + + ret = dell_z9100_iom_cpld_read(data,QSFP_MOD_PRS_REG1); + if(ret < 0) + return sprintf(buf, "read error"); + devdata |= (u16)(ret & 0xff) << 8; + + return sprintf(buf,"0x%04x\n",devdata); +} + +static ssize_t get_lpmode(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u16 devdata=0; + struct cpld_data *data = dev_get_drvdata(dev); + + ret = dell_z9100_iom_cpld_read(data,QSFP_LPMODE_REG0); + if(ret < 0) + return sprintf(buf, "read error"); + devdata = (u16)ret & 0xff; + + ret = dell_z9100_iom_cpld_read(data,QSFP_LPMODE_REG1); + if(ret < 0) + return sprintf(buf, "read error"); + devdata |= (u16)(ret & 0xff) << 8; + + return sprintf(buf,"0x%04x\n",devdata); +} + +static ssize_t get_reset(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u16 devdata=0; + struct cpld_data *data = dev_get_drvdata(dev); + + ret = dell_z9100_iom_cpld_read(data,QSFP_RST_CRTL_REG0); + if(ret < 0) + return sprintf(buf, "read error"); + devdata = (u16)ret & 0xff; + + ret = dell_z9100_iom_cpld_read(data,QSFP_RST_CRTL_REG1); + if(ret < 0) + return sprintf(buf, "read error"); + devdata |= (u16)(ret & 0xff) << 8; + + return sprintf(buf,"0x%04x\n",devdata); +} + +static ssize_t set_lpmode(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct cpld_data *data = dev_get_drvdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + dell_z9100_iom_cpld_write(data,QSFP_LPMODE_REG0,(u8)(devdata & 0xff)); + dell_z9100_iom_cpld_write(data,QSFP_LPMODE_REG1,(u8)((devdata >> 8) & 0xff)); + + return count; +} + +static ssize_t set_reset(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct cpld_data *data = dev_get_drvdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + dell_z9100_iom_cpld_write(data,QSFP_RST_CRTL_REG0,(u8)(devdata & 0xff)); + dell_z9100_iom_cpld_write(data,QSFP_RST_CRTL_REG1,(u8)((devdata >> 8) & 0xff)); + + return count; +} + +static DEVICE_ATTR(iom_cpld_vers,S_IRUGO,get_cpldver, NULL); +static DEVICE_ATTR(qsfp_modprs, S_IRUGO,get_modprs, NULL); +static DEVICE_ATTR(qsfp_lpmode, S_IRUGO | S_IWUSR,get_lpmode,set_lpmode); +static DEVICE_ATTR(qsfp_reset, S_IRUGO | S_IWUSR,get_reset, set_reset); + +static struct attribute *i2c_cpld_attrs[] = { + &dev_attr_qsfp_lpmode.attr, + &dev_attr_qsfp_reset.attr, + &dev_attr_qsfp_modprs.attr, + &dev_attr_iom_cpld_vers.attr, + NULL, +}; + +static struct attribute_group i2c_cpld_attr_grp = { + .attrs = i2c_cpld_attrs, +}; + +static int dell_z9100_iom_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_dbg(&client->dev, "i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + dev_info(&client->dev, "chip found- New\n"); + dell_z9100_iom_cpld_add_client(client); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &i2c_cpld_attr_grp); + if (status) { + printk(KERN_INFO "Cannot create sysfs\n"); + } + return 0; + +exit: + return status; +} + +static int dell_z9100_iom_cpld_remove(struct i2c_client *client) +{ + dell_z9100_iom_cpld_remove_client(client); + return 0; +} + + +static const struct i2c_device_id dell_z9100_iom_cpld_id[] = { + { "dell_z9100_iom_cpld", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, dell_z9100_iom_cpld_id); + +static struct i2c_driver dell_z9100_iom_cpld_driver = { + .driver = { + .name = "dell_z9100_iom_cpld", + }, + .probe = dell_z9100_iom_cpld_probe, + .remove = dell_z9100_iom_cpld_remove, + .id_table = dell_z9100_iom_cpld_id, +}; + + + +static int __init dell_z9100_iom_cpld_init(void) +{ + return i2c_add_driver(&dell_z9100_iom_cpld_driver); +} + +static void __exit dell_z9100_iom_cpld_exit(void) +{ + i2c_del_driver(&dell_z9100_iom_cpld_driver); +} + +MODULE_AUTHOR("Srideep Devireddy "); +MODULE_DESCRIPTION("dell_z9100_iom_cpld driver"); +MODULE_LICENSE("GPL"); + +module_init(dell_z9100_iom_cpld_init); +module_exit(dell_z9100_iom_cpld_exit); diff --git a/platform/broadcom/sonic-platform-modules-dell/z9100/scripts/check_qsfp.sh b/platform/broadcom/sonic-platform-modules-dell/z9100/scripts/check_qsfp.sh new file mode 100755 index 000000000000..38f62013e46b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/z9100/scripts/check_qsfp.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +#Usage: +# check_qsfp.sh qsfp10 +# check_qsfp.sh sffp2 + +# There are 34 optics ports. 32 QSFP28, 2 SFP+ +# QSFP28 is orgainized is 3 banks +# CPLD-2 QSFP-1-12 +# CPLD-3 QSFP-13-22 +# CPLD-4 QSFP-23-32 +# SFF+ 2 ports on CPLD-4 + +optics=$1 +cpld_addr="003e" + +if [ `expr match $optics qsfp` -ne 0 ] +then + type=qsfp + id=${optics:4} + + if [ $id -le 0 -o $id -gt 32 ] + then + echo "Invalid QSFP id" + return -1 + fi + if [ $id -le 12 ] + then + #cpld=2 + cpld_bus=15 + elif [ $id -le 22 ] + then + #cpld=3 + cpld_bus=16 + else + #cpld=4 + cpld_bus=17 + fi + + if [ $id -le 8 ] + then + let qsfp_bus=42+$id + elif [ $id -le 16 ] + then + let qsfp_bus=34+${id}-8 + elif [ $id -le 24 ] + then + let qsfp_bus=26+${id}-16 + else + let qsfp_bus=18+${id}-24 + fi +else + type=sff + id=${optics:3} + cpld=4 +fi + +echo "Preence:`cat /sys/class/i2c-dev/i2c-${cpld_bus}/device/${cpld_bus}-${cpld_addr}/qsfp_modprs`" +echo "Hexdump:" +cat /sys/bus/i2c/devices/${qsfp_bus}-0050/eeprom | hexdump -C diff --git a/platform/broadcom/sonic-platform-modules-dell/z9100/scripts/platform_sensors.py b/platform/broadcom/sonic-platform-modules-dell/z9100/scripts/platform_sensors.py new file mode 100755 index 000000000000..763e65772282 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/z9100/scripts/platform_sensors.py @@ -0,0 +1,324 @@ +#!/usr/bin/python +# +# On Z9100, the Platform Management Controller runs the +# thermal algorithm. It provides a mailbox for the Host +# to query relevant thermals. The dell_mailbox module +# provides the sysfs support for the following objects: +# * onboard temperature sensors +# * FAN trays +# * PSU + +import os +import sys +import logging + +Z9100_MAX_FAN_TRAYS = 5 +Z9100_MAX_PSUS = 2 +S6100_MAX_IOMS = 4 + +MAILBOX_DIR = "/sys/devices/platform/SMF.512/hwmon/hwmon1" + +# Get a mailbox register + + +def get_pmc_register(reg_name): + retval = 'ERR' + mb_reg_file = MAILBOX_DIR+'/'+reg_name + + if (not os.path.isfile(mb_reg_file)): + print mb_reg_file, 'not found !' + return retval + + try: + with open(mb_reg_file, 'r') as fd: + retval = fd.read() + except Exception as error: + logging.error("Unable to open ", mb_reg_file, "file !") + + retval = retval.rstrip('\r\n') + retval = retval.lstrip(" ") + return retval + +logging.basicConfig(level=logging.DEBUG) + +if (os.path.isdir(MAILBOX_DIR)): + print 'dell-s6100-lpc' + print 'Adapter: S6100 Platform Management Controller' +else: + logging.error('S6100 Platform Management Controller module not loaded !') + # sys.exit(0) + +# Print the information for temperature sensors + + +def print_temperature_sensors(): + print("\nOnboard Temperature Sensors:") + print ' CPU: ',\ + int(get_pmc_register('temp1_input'))/1000, 'C' + print ' BCM56960 (PSU side): ',\ + int(get_pmc_register('temp2_input'))/1000, 'C' + print ' System Outlet 1 (switch board): ',\ + int(get_pmc_register('temp3_input'))/1000, 'C' + print ' BCM56960 (IO side): ',\ + int(get_pmc_register('temp4_input'))/1000, 'C' + print ' System Outlet 2 (CPU board): ',\ + int(get_pmc_register('temp9_input'))/1000, 'C' + print ' System Inlet Left (IO side): ',\ + int(get_pmc_register('temp6_input'))/1000, 'C' + +print_temperature_sensors() + + +# Print the information for voltage sensors + + +def print_voltage_sensors(): + print("\nOnboard Voltage Sensors:") + + print ' CPU XP3R3V_EARLY ',\ + float(get_pmc_register('in1_input'))/1000, 'V' + print '\n CPU XP5R0V_CP ',\ + float(get_pmc_register('in2_input'))/1000, 'V' + print ' CPU XP3R3V_STD ',\ + float(get_pmc_register('in3_input'))/1000, 'V' + print ' CPU XP3R3V_CP ',\ + float(get_pmc_register('in4_input'))/1000, 'V' + print ' CPU XP0R75V_VTT_A ',\ + float(get_pmc_register('in5_input'))/1000, 'V' + print ' CPU XPPR75V_VTT_B ',\ + float(get_pmc_register('in6_input'))/1000, 'V' + print ' CPU XP1R07V_CPU ',\ + float(get_pmc_register('in7_input'))/1000, 'V' + print ' CPU XP1R0V_CPU ',\ + float(get_pmc_register('in8_input'))/1000, 'V' + print ' CPU XP12R0V ',\ + float(get_pmc_register('in9_input'))/1000, 'V' + print ' CPU VDDR_CPU_2 ',\ + float(get_pmc_register('in10_input'))/1000, 'V' + print ' CPU VDDR_CPU_1 ',\ + float(get_pmc_register('in11_input'))/1000, 'V' + print ' CPU XP1R5V_CLK ',\ + float(get_pmc_register('in12_input'))/1000, 'V' + print ' CPU XP1R8V_CPU ',\ + float(get_pmc_register('in13_input'))/1000, 'V' + print ' CPU XP1R0V_CPU_VNN ',\ + float(get_pmc_register('in14_input'))/1000, 'V' + print ' CPU XP1R0V_CPU_VCC ',\ + float(get_pmc_register('in15_input'))/1000, 'V' + print ' CPU XP1R5V_EARLY ',\ + float(get_pmc_register('in16_input'))/1000, 'V' + print ' SW XP3R3V_MON ',\ + float(get_pmc_register('in17_input'))/1000, 'V' + print ' SW XP1R8V_MON ',\ + float(get_pmc_register('in19_input'))/1000, 'V' + print ' SW XP1R2V_MON ',\ + float(get_pmc_register('in20_input'))/1000, 'V' + print ' SW XP1R0V_SW_MON ',\ + float(get_pmc_register('in21_input'))/1000, 'V' + print ' SW XP1R0V_ROV_SW_MON ',\ + float(get_pmc_register('in22_input'))/1000, 'V' + print ' SW XR1R0V_BCM84752_MON ',\ + float(get_pmc_register('in23_input'))/1000, 'V' + print ' SW XP5V_MB_MON ',\ + float(get_pmc_register('in24_input'))/1000, 'V' + print ' SW XP1R8V_FPGA_MON ',\ + float(get_pmc_register('in25_input'))/1000, 'V' + print ' SW XP3R3V_FPGA_MON ',\ + float(get_pmc_register('in26_input'))/1000, 'V' + print ' SW XP3R3V_EARLY_MON ',\ + float(get_pmc_register('in27_input'))/1000, 'V' + print ' SW XP1R0V_ ',\ + float(get_pmc_register('curr21_input'))/10000, 'A' + print ' SW XP1R0V_ROV ',\ + float(get_pmc_register('curr22_input'))/10000, 'A' + + + +print_voltage_sensors() + +# Print the information for 1 Fan Tray + + +def print_fan_tray(tray): + + Fan_Status = ['Normal', 'Abnormal'] + Airflow_Direction = ['B2F', 'F2B'] + + print ' Fan Tray ' + str(tray) + ':' + + if (tray == 1): + fan1_speed = get_pmc_register('fan1_input') + fan2_speed = get_pmc_register('fan2_input') + air_flow_reg = int(get_pmc_register('fan1_airflow'), 16) + fan1_status = 0 if fan1_speed >= 1000 else 1 + fan2_status = 0 if fan2_speed >= 1000 else 1 + + print ' Fan1 Speed: ', fan1_speed, 'RPM' + print ' Fan2 Speed: ', fan2_speed, 'RPM' + print ' Fan1 State: ', Fan_Status[fan1_status] + print ' Fan2 State: ', Fan_Status[fan2_status] + print ' Air Flow: ', Airflow_Direction[air_flow_reg] + elif (tray == 2): + fan1_speed = get_pmc_register('fan3_input') + fan2_speed = get_pmc_register('fan4_input') + air_flow_reg = int(get_pmc_register('fan3_airflow'), 16) + fan1_status = 0 if fan1_speed >= 1000 else 1 + fan2_status = 0 if fan2_speed >= 1000 else 1 + + print ' Fan1 Speed: ', fan1_speed, 'RPM' + print ' Fan2 Speed: ', fan2_speed, 'RPM' + print ' Fan1 State: ', Fan_Status[fan1_status] + print ' Fan2 State: ', Fan_Status[fan2_status] + print ' Air Flow: ', Airflow_Direction[air_flow_reg] + elif (tray == 3): + fan1_speed = get_pmc_register('fan5_input') + fan2_speed = get_pmc_register('fan6_input') + air_flow_reg = int(get_pmc_register('fan5_airflow'), 16) + fan1_status = 0 if fan1_speed >= 1000 else 1 + fan2_status = 0 if fan2_speed >= 1000 else 1 + + print ' Fan1 Speed: ', fan1_speed, 'RPM' + print ' Fan2 Speed: ', fan2_speed, 'RPM' + print ' Fan1 State: ', Fan_Status[fan1_status] + print ' Fan2 State: ', Fan_Status[fan2_status] + print ' Air Flow: ', Airflow_Direction[air_flow_reg] + elif (tray == 4): + fan1_speed = get_pmc_register('fan7_input') + fan2_speed = get_pmc_register('fan8_input') + air_flow_reg = int(get_pmc_register('fan7_airflow'), 16) + fan1_status = 0 if fan1_speed >= 1000 else 1 + fan2_status = 0 if fan2_speed >= 1000 else 1 + + print ' Fan1 Speed: ', fan1_speed, 'RPM' + print ' Fan2 Speed: ', fan2_speed, 'RPM' + print ' Fan1 State: ', Fan_Status[fan1_status] + print ' Fan2 State: ', Fan_Status[fan2_status] + print ' Air Flow: ', Airflow_Direction[air_flow_reg] + elif (tray == 5): + fan1_speed = get_pmc_register('fan9_input') + fan2_speed = get_pmc_register('fan10_input') + air_flow_reg = int(get_pmc_register('fan9_airflow'), 16) + fan1_status = 0 if fan1_speed >= 1000 else 1 + fan2_status = 0 if fan2_speed >= 1000 else 1 + + print ' Fan1 Speed: ', fan1_speed, 'RPM' + print ' Fan2 Speed: ', fan2_speed, 'RPM' + print ' Fan1 State: ', Fan_Status[fan1_status] + print ' Fan2 State: ', Fan_Status[fan2_status] + print ' Air Flow: ', Airflow_Direction[air_flow_reg] + + +print('\nFan Trays:') +fan_tray_presence = get_pmc_register('fan_tray_presence') + +if (fan_tray_presence != 'ERR'): + fan_tray_presence = int(fan_tray_presence, 16) + + for tray in range(0, Z9100_MAX_FAN_TRAYS): + if (fan_tray_presence & (1 << tray)): + print_fan_tray(tray + 1) + else: + print '\n Fan Tray ' + str(tray + 1) + ': Not present' +else: + logging.error('Unable to read FAN presence') + +# Print the information for PSU1, PSU2 + + +def print_psu(psu): + Psu_Type = ['Normal', 'Mismatch'] + Psu_Input_Type = ['AC', 'DC'] + PSU_STATUS_TYPE_BIT = 4 + PSU_STATUS_INPUT_TYPE_BIT = 1 + PSU_FAN_PRESENT_BIT = 2 + PSU_FAN_STATUS_BIT = 1 + PSU_FAN_AIR_FLOW_BIT = 0 + Psu_Fan_Presence = ['Present', 'Absent'] + Psu_Fan_Status = ['Normal', 'Abnormal'] + Psu_Fan_Airflow = ['B2F', 'F2B'] + + + print ' PSU ' + str(psu) + ':' + if (psu == 1): + psu_status = int(get_pmc_register('psu1_presence'), 16) + else: + psu_status = int(get_pmc_register('psu2_presence'), 16) + + psu_input_type = (psu_status & (1 << PSU_STATUS_INPUT_TYPE_BIT)) >>\ + PSU_STATUS_INPUT_TYPE_BIT + psu_type = (psu_status & (1 << PSU_STATUS_TYPE_BIT)) >>\ + PSU_STATUS_TYPE_BIT + + print ' Input: ', Psu_Input_Type[psu_input_type] + print ' Type: ', Psu_Type[psu_type] + + # PSU FAN details + if (psu == 1): + print ' FAN Speed: ', get_pmc_register('fan11_input'), 'RPM' + psu_fan_airflow = int(get_pmc_register('fan11_airflow')) + psu_fan_status = int(get_pmc_register('fan11_alarm')) + psu_fan_present = int(get_pmc_register('fan11_fault')) + input_voltage = float(get_pmc_register('in29_input')) / 1000 + output_voltage = float(get_pmc_register('in30_input')) / 1000 + input_current = float(get_pmc_register('curr601_input')) / 100 + output_current = float(get_pmc_register('curr602_input')) /100 + input_power = float(get_pmc_register('power1_input')) / 1000000 + output_power = float(get_pmc_register('power2_input')) / 1000000 + if (input_power != 0): + psu_fan_temp = int(get_pmc_register('temp14_input'))/1000 + else: + print ' FAN Speed: ', get_pmc_register('fan12_input'), 'RPM' + psu_fan_airflow = int(get_pmc_register('fan12_airflow')) + psu_fan_status = int(get_pmc_register('fan12_alarm')) + psu_fan_present = int(get_pmc_register('fan12_fault')) + input_voltage = float(get_pmc_register('in31_input')) / 1000 + output_voltage = float(get_pmc_register('in32_input')) / 1000 + input_current = float(get_pmc_register('curr701_input')) / 100 + output_current = float(get_pmc_register('curr702_input')) / 100 + input_power = float(get_pmc_register('power3_input')) / 1000000 + output_power = float(get_pmc_register('power4_input')) / 1000000 + if (input_power != 0): + psu_fan_temp = int(get_pmc_register('temp15_input'))/1000 + + print ' FAN: ', Psu_Fan_Presence[psu_fan_present] + print ' FAN Status: ', Psu_Fan_Status[psu_fan_status] + print ' FAN AIRFLOW: ', Psu_Fan_Airflow[psu_fan_airflow] + + # PSU input & output monitors + print ' Input Voltage: %6.2f' % (input_voltage), 'V' + + print ' Output Voltage: %6.2f' % (output_voltage), 'V' + + print ' Input Current: %6.2f' % (input_current), 'A' + + print ' Output Current: %6.2f' % (output_current), 'A' + + print ' Input Power: %6.2f' % (input_power), 'W' + + print ' Output Power: %6.2f' % (output_power), 'W' + + # PSU firmware gives spurious temperature reading without input power + if (input_power != 0): + print ' Temperature: ', psu_fan_temp, 'C' + else: + print ' Temperature: ', 'NA' + +print('\nPSUs:') +for psu in range(1, Z9100_MAX_PSUS + 1): + + if (psu == 1): + psu_status = get_pmc_register('psu1_presence') + else: + psu_status = get_pmc_register('psu2_presence') + + if (psu_status != 'ERR'): + psu_status = int(psu_status, 16) + + if (~psu_status & 0b1): + print_psu(psu) + else: + print '\n PSU ', psu, 'Not present' + else: + logging.error('Unable to check PSU presence') + +print '\n Total Power: ', get_pmc_register('current_total_power'), 'W' diff --git a/platform/broadcom/sonic-platform-modules-dell/z9100/scripts/sensors b/platform/broadcom/sonic-platform-modules-dell/z9100/scripts/sensors new file mode 100755 index 000000000000..030d636a1565 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/z9100/scripts/sensors @@ -0,0 +1,7 @@ +#!/bin/bash +docker exec -i pmon sensors "$@" + +#To probe sensors not part of lm-sensors +if [ -r /usr/local/bin/platform_sensors.py ]; then + python /usr/local/bin/platform_sensors.py +fi diff --git a/platform/broadcom/sonic-platform-modules-dell/z9100/scripts/z9100_platform.sh b/platform/broadcom/sonic-platform-modules-dell/z9100/scripts/z9100_platform.sh new file mode 100755 index 000000000000..6b62144eedac --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/z9100/scripts/z9100_platform.sh @@ -0,0 +1,169 @@ +#!/bin/bash + +#platform init script for Dell Z9100 + +source dell_i2c_utils.sh + +init_devnum() { + found=0 + for devnum in 0 1; do + devname=`cat /sys/bus/i2c/devices/i2c-${devnum}/name` + # iSMT adapter can be at either dffd0000 or dfff0000 + if [[ $devname == 'SMBus iSMT adapter at '* ]]; then + found=1 + break + fi + done + + [[ $found -eq 0 ]] && echo "cannot find iSMT" && exit 1 +} + +# Attach/Detach CPU board mux @ 0x70 +cpu_board_mux() { + case $1 in + "new_device") i2c_config "echo pca9547 0x70 > /sys/bus/i2c/devices/i2c-${devnum}/$1" + ;; + "delete_device") i2c_config "echo 0x70 > /sys/bus/i2c/devices/i2c-${devnum}/$1" + ;; + *) echo "z9100_platform: cpu_board_mux: invalid command !" + ;; + esac +} + +# Attach/Detach switch board MUX to IOM CPLDs @ 0x71 +switch_board_mux() { + case $1 in + "new_device") i2c_config "echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-4/$1" + ;; + "delete_device") i2c_config "echo 0x71 > /sys/bus/i2c/devices/i2c-4/$1" + ;; + *) echo "z9100_platform: switch_board_mux : invalid command !" + ;; + esac +} + +# Attach/Detach syseeprom on CPU board +sys_eeprom() { + case $1 in + "new_device") i2c_config "echo 24c02 0x50 > /sys/bus/i2c/devices/i2c-2/$1" + ;; + "delete_device") i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-2/$1" + ;; + *) echo "z9100_platform: sys_eeprom : invalid command !" + ;; + esac +} + +#Attach/Detach cpld devices to drivers for each iom +switch_board_cpld() { + case $1 in + "new_device") + for ((i=14;i<=17;i++)); + do + i2c_config "echo dell_z9100_iom_cpld 0x3e > /sys/bus/i2c/devices/i2c-$i/$1" + done + ;; + "delete_device") + for ((i=14;i<=17;i++)); + do + i2c_config "echo 0x3e > /sys/bus/i2c/devices/i2c-$i/$1" + done + ;; + *) echo "z9100_platform: switch_board_cpld : invalid command !" + ;; + esac +} + +#Attach/Detach the MUX connecting all QSFPs +switch_board_qsfp_mux() { + case $1 in + "new_device") + for ((i=9;i>=6;i--)); + do + # 0x71 mux on the IOM 1 + mux_index=$(expr $i - 5) + echo "Attaching PCA9548 $mux_index" + i2c_config "echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-$i/$1" + done + ;; + "delete_device") + for ((i=9;i>=6;i--)); + do + # 0x71 mux on the IOM 1 + mux_index=$(expr $i - 5) + echo "Detaching PCA9548 $mux_index" + i2c_config "echo 0x71 > /sys/bus/i2c/devices/i2c-$devnum/i2c-$i/$1" + done + ;; + *) echo "z9100_platform: switch_board_qsfp_mux: invalid command !" + ;; + esac +} + +#Attach/Detach the SFP modules on PCA9548_2 +switch_board_sfp() { + case $1 in + "new_device") i2c_config "echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-11/$1" + i2c_config "echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-12/$1" + ;; + "delete_device") i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-11/$1" + i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-12/$1" + ;; + *) echo "z9100_platform: switch_board_sfp: invalid command !" + ;; + esac +} + +# Attach/Detach 32 instances of EEPROM driver QSFP ports on IO module 1 +#eeprom can dump data using below command +switch_board_qsfp() { + case $1 in + "new_device") + for ((i=18;i<=49;i++)); + do + i2c_config "echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-$i/$1" + done + ;; + "delete_device") + for ((i=18;i<=49;i++)); + do + i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-$i/$1" + done + ;; + + *) echo "z9100_platform: switch_board_qsfp: invalid command !" + ;; + esac +} + +init_devnum + +if [[ "$1" == "init" ]]; then + modprobe i2c-dev + modprobe i2c-mux-pca954x force_deselect_on_exit=1 + modprobe dell_mailbox + modprobe dell_z9100_cpld + + cpu_board_mux "new_device" + switch_board_mux "new_device" + sys_eeprom "new_device" + switch_board_cpld "new_device" + switch_board_qsfp_mux "new_device" + switch_board_sfp "new_device" + switch_board_qsfp "new_device" +elif [[ "$1" == "deinit" ]]; then + switch_board_sfp "delete_device" + switch_board_cpld "delete_device" + switch_board_mux "delete_device" + sys_eeprom "delete_device" + switch_board_qsfp "delete_device" + switch_board_qsfp_mux "delete_device" + cpu_board_mux "delete_device" + + modprobe -r dell_z9100_cpld + modprobe -r dell_mailbox + modprobe -r i2c-mux-pca954x + modprobe -r i2c-dev +else + echo "z9100_platform : Invalid option !" +fi diff --git a/platform/broadcom/sonic-platform-modules-ingrasys b/platform/broadcom/sonic-platform-modules-ingrasys deleted file mode 160000 index f6e0fa9860c6..000000000000 --- a/platform/broadcom/sonic-platform-modules-ingrasys +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f6e0fa9860c6d1bd9274b091147d4fc06a0597c3 diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/.gitignore b/platform/broadcom/sonic-platform-modules-ingrasys/.gitignore new file mode 100644 index 000000000000..f805e810e5c6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/.gitignore @@ -0,0 +1,33 @@ +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/LICENSE b/platform/broadcom/sonic-platform-modules-ingrasys/LICENSE new file mode 100644 index 000000000000..9cecc1d4669e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/changelog b/platform/broadcom/sonic-platform-modules-ingrasys/debian/changelog new file mode 100644 index 000000000000..338234f97c45 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/changelog @@ -0,0 +1,22 @@ +platform-driver (1.1.0) unstable; urgency=low + + * Add support for s9200-64x + + -- developer Fri, 26 May 2017 11:00:00 +0800 + +platform-driver (1.1.0) unstable; urgency=low + + * Add support for s8810-32q + + -- developer Fri, 26 May 2017 11:00:00 +0800 +platform-driver (1.1.0) unstable; urgency=low + + * Add support for s8900 series + + -- developer Wed, 29 Mar 2017 11:00:00 +0800 + +platform-driver (1.0.0) unstable; urgency=low + + * Initial release (Closes: #nnnn) + + -- developer Wed, 05 Oct 2016 16:30:45 +0800 diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/compat b/platform/broadcom/sonic-platform-modules-ingrasys/debian/compat new file mode 100644 index 000000000000..ec635144f600 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/compat @@ -0,0 +1 @@ +9 diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/control b/platform/broadcom/sonic-platform-modules-ingrasys/debian/control new file mode 100644 index 000000000000..99c87f772ed5 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/control @@ -0,0 +1,26 @@ +Source: platform-driver +Section: unknown +Priority: optional +Maintainer: Wade He +Build-Depends: debhelper (>= 9), dh-systemd +Standards-Version: 1.0.0 + +Package: sonic-platform-ingrasys-s9100 +Architecture: amd64 +Description: This package contains S9100 platform driver utility for SONiC project. + +Package: sonic-platform-ingrasys-s8900-64xc +Architecture: amd64 +Description: This package contains S8900-64XC platform driver utility for SONiC project. + +Package: sonic-platform-ingrasys-s8900-54xc +Architecture: amd64 +Description: This package contains S8900-54XC platform driver utility for SONiC project. + +Package: sonic-platform-ingrasys-s8810-32q +Architecture: amd64 +Description: This package contains S8810-32Q platform driver utility for SONiC project. + +Package: sonic-platform-ingrasys-s9200-64x +Architecture: amd64 +Description: This package contains S9200-64X platform driver utility for SONiC project. diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/rules b/platform/broadcom/sonic-platform-modules-ingrasys/debian/rules new file mode 100644 index 000000000000..352dd55afbd7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/rules @@ -0,0 +1,80 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +export INSTALL_MOD_DIR:=extra + +PACKAGE_PRE_NAME := sonic-platform-ingrasys +KVERSION ?= $(shell uname -r) +KERNEL_SRC := /lib/modules/$(KVERSION) +MOD_SRC_DIR:= $(shell pwd) +MODULE_DIRS:= s9100 s8900-64xc s8900-54xc s8810-32q s9200-64x +MODULE_DIR := modules +UTILS_DIR := utils +SERVICE_DIR := service +CONF_DIR := conf + +%: + dh $@ --with systemd + +clean: + dh_testdir + dh_testroot + dh_clean + +build: + #make modules -C $(KERNEL_SRC)/build M=$(MODULE_SRC) + (for mod in $(MODULE_DIRS); do \ + make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + done) + +binary: binary-arch binary-indep + # Nothing to do + +binary-arch: + # Nothing to do + +#install: build + #dh_testdir + #dh_testroot + #dh_clean -k + #dh_installdirs + +binary-indep: + dh_testdir + dh_installdirs + + # Custom package commands + (for mod in $(MODULE_DIRS); do \ + mkdir debian/tmp/usr/sbin -p; \ + mkdir debian/tmp/lib/systemd/system -p; \ + mkdir debian/tmp/etc -p; \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} $(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$${mod}/$(MODULE_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$${mod}/$(UTILS_DIR)/*.sh debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/sbin/; \ + cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/*.service debian/$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system/; \ + done) + # Resuming debhelper scripts + dh_testroot + dh_install + dh_installchangelogs + dh_installdocs + dh_systemd_enable + dh_installinit + dh_systemd_start + dh_link + dh_fixperms + dh_compress + dh_strip + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb +.PHONY: build binary binary-arch binary-indep clean diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.dirs b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.dirs new file mode 100644 index 000000000000..bec29f195770 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.dirs @@ -0,0 +1,3 @@ +usr/sbin +lib/systemd/system +etc/ \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.install b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.install new file mode 100644 index 000000000000..38efbef5d864 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.install @@ -0,0 +1,3 @@ +lib/systemd/ +usr/sbin/ +etc/ \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.postinst b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.postinst new file mode 100644 index 000000000000..6a94f8327559 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.postinst @@ -0,0 +1,43 @@ +# Automatically added by dh_systemd_enable +# This will only remove masks created by d-s-h on package removal. +deb-systemd-helper unmask s8810-32q-monitor.service >/dev/null || true +deb-systemd-helper unmask qsfp-monitor.service >/dev/null || true +# Generate kernel modules.dep and map files for add eeprom_mb. +depmod -a || true +# was-enabled defaults to true, so new installations run enable. +if deb-systemd-helper --quiet was-enabled s8810-32q-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable s8810-32q-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state s8810-32q-monitor.service >/dev/null || true +fi +if deb-systemd-helper --quiet was-enabled qsfp-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable qsfp-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state qsfp-monitor.service >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s8810-32q-monitor" ]; then + update-rc.d s8810-32q-monitor defaults >/dev/null + invoke-rc.d s8810-32q-monitor start || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + update-rc.d qsfp-monitor defaults >/dev/null + invoke-rc.d qsfp-monitor start || exit $? +fi +# End automatically added section +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + deb-systemd-invoke start s8810-32q-monitor.service >/dev/null || true + deb-systemd-invoke start qsfp-monitor.service >/dev/null || true +fi +# End automatically added section diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.postrm b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.postrm new file mode 100644 index 000000000000..a1049b153eef --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.postrm @@ -0,0 +1,38 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + fi +# End automatically added section +# Automatically added by dh_installinit +if [ "$1" = "purge" ] ; then + update-rc.d s8810-32q-monitor remove >/dev/null + update-rc.d qsfp-monitor remove >/dev/null +fi + + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [ -d /run/systemd/system ] ; then + systemctl --system daemon-reload >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_systemd_enable +if [ "$1" = "remove" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper mask s8810-32q-monitor.service >/dev/null + deb-systemd-helper mask qsfp-monitor.service >/dev/null + fi +fi + +if [ "$1" = "purge" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper purge s8810-32q-monitor.service >/dev/null + deb-systemd-helper unmask s8810-32q-monitor.service >/dev/null + deb-systemd-helper purge qsfp-monitor.service >/dev/null + deb-systemd-helper unmask qsfp-monitor.service >/dev/null + fi +fi +# Generate kernel modules.dep and map files for remove eeprom_mb. +depmod -a || true +# End automatically added section + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.prerm b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.prerm new file mode 100644 index 000000000000..5a6581e5f410 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.prerm @@ -0,0 +1,17 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + deb-systemd-invoke stop s8810-32q-monitor.service >/dev/null + deb-systemd-invoke stop qsfp-monitor.service >/dev/null +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s8810-32q-monitor" ]; then + invoke-rc.d s8810-32q-monitor stop || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + invoke-rc.d qsfp-monitor stop || exit $? +fi +# Driver deinit +/usr/sbin/i2c_utils.sh i2c_deinit +# End automatically added section + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.upstart b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.upstart new file mode 100644 index 000000000000..f907d1bb51e5 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8810-32q.upstart @@ -0,0 +1,6 @@ +description "SONiC platform service" + +respawn + +exec /usr/sbin/s8810_32q_monitor.sh +exec /usr/sbin/qsfp_monitor.sh diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.dirs b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.dirs new file mode 100644 index 000000000000..bec29f195770 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.dirs @@ -0,0 +1,3 @@ +usr/sbin +lib/systemd/system +etc/ \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.install b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.install new file mode 100644 index 000000000000..38efbef5d864 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.install @@ -0,0 +1,3 @@ +lib/systemd/ +usr/sbin/ +etc/ \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.postinst b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.postinst new file mode 100644 index 000000000000..d2dc4ea518fb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.postinst @@ -0,0 +1,43 @@ +# Automatically added by dh_systemd_enable +# This will only remove masks created by d-s-h on package removal. +deb-systemd-helper unmask s8900-54xc-monitor.service >/dev/null || true +# Generate kernel modules.dep and map files for add eeprom_mb. +depmod -a || true +# was-enabled defaults to true, so new installations run enable. +if deb-systemd-helper --quiet was-enabled s8900-54xc-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable s8900-54xc-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state s8900-54xc-monitor.service >/dev/null || true +fi +if deb-systemd-helper --quiet was-enabled qsfp-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable qsfp-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state qsfp-monitor.service >/dev/null || true +fi + +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s8900-54xc-monitor" ]; then + update-rc.d s8900-54xc-monitor defaults >/dev/null + invoke-rc.d s8900-54xc-monitor start || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + update-rc.d qsfp-monitor defaults >/dev/null + invoke-rc.d qsfp-monitor start || exit $? +fi +# End automatically added section +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + deb-systemd-invoke start s8900-54xc-monitor.service >/dev/null || true + deb-systemd-invoke start qsfp-monitor.service >/dev/null || true +fi +# End automatically added section diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.postrm b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.postrm new file mode 100644 index 000000000000..b4e4ede5d88d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.postrm @@ -0,0 +1,38 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + fi +# End automatically added section +# Automatically added by dh_installinit +if [ "$1" = "purge" ] ; then + update-rc.d s8900-54xc-monitor remove >/dev/null + update-rc.d qsfp-monitor remove >/dev/null +fi + + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [ -d /run/systemd/system ] ; then + systemctl --system daemon-reload >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_systemd_enable +if [ "$1" = "remove" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper mask s8900-54xc-monitor.service >/dev/null + deb-systemd-helper mask qsfp-monitor.service >/dev/null + fi +fi + +if [ "$1" = "purge" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper purge s8900-54xc-monitor.service >/dev/null + deb-systemd-helper unmask s8900-54xc-monitor.service >/dev/null + deb-systemd-helper purge qsfp-monitor.service >/dev/null + deb-systemd-helper unmask qsfp-monitor.service >/dev/null + fi +fi +# Generate kernel modules.dep and map files for remove eeprom_mb. +depmod -a || true +# End automatically added section + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.prerm b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.prerm new file mode 100644 index 000000000000..b35262b16cf8 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.prerm @@ -0,0 +1,17 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + deb-systemd-invoke stop s8900-54xc-monitor.service >/dev/null + deb-systemd-invoke stop qsfp-monitor.service >/dev/null +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s8900-54xc-monitor" ]; then + invoke-rc.d s8900-54xc-monitor stop || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + invoke-rc.d qsfp-monitor stop || exit $? +fi +# Driver deinit +/usr/sbin/i2c_utils.sh i2c_deinit +# End automatically added section + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.upstart b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.upstart new file mode 100644 index 000000000000..cfdfc490fcc7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-54xc.upstart @@ -0,0 +1,6 @@ +description "SONiC platform service" + +respawn + +exec /usr/sbin/s8900_54xc_monitor.sh +exec /usr/sbin/qsfp_monitor.sh diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.dirs b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.dirs new file mode 100644 index 000000000000..be337bc5ea1f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.dirs @@ -0,0 +1,3 @@ +usr/sbin +lib/systemd/system +etc/ diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.install b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.install new file mode 100644 index 000000000000..1cf6f97b306f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.install @@ -0,0 +1,3 @@ +lib/systemd/ +usr/sbin/ +etc/ diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.postinst b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.postinst new file mode 100644 index 000000000000..dfc789e964bd --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.postinst @@ -0,0 +1,42 @@ +# Automatically added by dh_systemd_enable +# This will only remove masks created by d-s-h on package removal. +deb-systemd-helper unmask s8900-64xc-monitor.service >/dev/null || true +# Generate kernel modules.dep and map files for add eeprom_mb. +depmod -a || true +# was-enabled defaults to true, so new installations run enable. +if deb-systemd-helper --quiet was-enabled s8900-64xc-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable s8900-64xc-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state s8900-64xc-monitor.service >/dev/null || true +fi +if deb-systemd-helper --quiet was-enabled qsfp-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable qsfp-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state qsfp-monitor.service >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s8900-64xc-monitor" ]; then + update-rc.d s8900-64xc-monitor defaults >/dev/null + invoke-rc.d s8900-64xc-monitor start || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + update-rc.d qsfp-monitor defaults >/dev/null + invoke-rc.d qsfp-monitor start || exit $? +fi +# End automatically added section +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + deb-systemd-invoke start s8900-64xc-monitor.service >/dev/null || true + deb-systemd-invoke start qsfp-monitor.service >/dev/null || true +fi +# End automatically added section diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.postrm b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.postrm new file mode 100644 index 000000000000..e3dddecc088b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.postrm @@ -0,0 +1,38 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + fi +# End automatically added section +# Automatically added by dh_installinit +if [ "$1" = "purge" ] ; then + update-rc.d s8900-64xc-monitor remove >/dev/null + update-rc.d qsfp-monitor remove >/dev/null +fi + + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [ -d /run/systemd/system ] ; then + systemctl --system daemon-reload >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_systemd_enable +if [ "$1" = "remove" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper mask s8900-64xc-monitor.service >/dev/null + deb-systemd-helper mask qsfp-monitor.service >/dev/null + fi +fi + +if [ "$1" = "purge" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper purge s8900-64xc-monitor.service >/dev/null + deb-systemd-helper unmask s8900-64xc-monitor.service >/dev/null + deb-systemd-helper purge qsfp-monitor.service >/dev/null + deb-systemd-helper unmask qsfp-monitor.service >/dev/null + fi +fi +# Generate kernel modules.dep and map files for remove eeprom_mb. +depmod -a || true +# End automatically added section + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.prerm b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.prerm new file mode 100644 index 000000000000..2168a6c76245 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.prerm @@ -0,0 +1,15 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + deb-systemd-invoke stop s8900-64xc-monitor.service >/dev/null + deb-systemd-invoke stop qsfp-monitor.service >/dev/null +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s8900-64xc-monitor" ]; then + invoke-rc.d s8900-64xc-monitor stop || exit $? + invoke-rc.d qsfp-monitor stop || exit $? +fi +# Driver deinit +/usr/sbin/i2c_utils.sh i2c_deinit +# End automatically added section + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.upstart b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.upstart new file mode 100644 index 000000000000..25510457ecd8 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s8900-64xc.upstart @@ -0,0 +1,6 @@ +description "SONiC platform service" + +respawn + +exec /usr/sbin/s8900_64xc_monitor.sh +exec /usr/sbin/qsfp-monitor.sh diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.dirs b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.dirs new file mode 100644 index 000000000000..bec29f195770 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.dirs @@ -0,0 +1,3 @@ +usr/sbin +lib/systemd/system +etc/ \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.install b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.install new file mode 100644 index 000000000000..38efbef5d864 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.install @@ -0,0 +1,3 @@ +lib/systemd/ +usr/sbin/ +etc/ \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.postinst b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.postinst new file mode 100644 index 000000000000..a9bc64e26c05 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.postinst @@ -0,0 +1,43 @@ +# Automatically added by dh_systemd_enable +# This will only remove masks created by d-s-h on package removal. +deb-systemd-helper unmask s9100-monitor.service >/dev/null || true +deb-systemd-helper unmask qsfp-monitor.service >/dev/null || true +# Generate kernel modules.dep and map files for add eeprom_mb. +depmod -a || true +# was-enabled defaults to true, so new installations run enable. +if deb-systemd-helper --quiet was-enabled s9100-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable s9100-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state s9100-monitor.service >/dev/null || true +fi +if deb-systemd-helper --quiet was-enabled qsfp-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable qsfp-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state qsfp-monitor.service >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s9100-monitor" ]; then + update-rc.d s9100-monitor defaults >/dev/null + invoke-rc.d s9100-monitor start || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + update-rc.d qsfp-monitor defaults >/dev/null + invoke-rc.d qsfp-monitor start || exit $? +fi +# End automatically added section +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + deb-systemd-invoke start s9100-monitor.service >/dev/null || true + deb-systemd-invoke start qsfp-monitor.service >/dev/null || true +fi +# End automatically added section diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.postrm b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.postrm new file mode 100644 index 000000000000..669179b11e50 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.postrm @@ -0,0 +1,38 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + fi +# End automatically added section +# Automatically added by dh_installinit +if [ "$1" = "purge" ] ; then + update-rc.d s9100-monitor remove >/dev/null + update-rc.d qsfp-monitor remove >/dev/null +fi + + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [ -d /run/systemd/system ] ; then + systemctl --system daemon-reload >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_systemd_enable +if [ "$1" = "remove" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper mask s9100-monitor.service >/dev/null + deb-systemd-helper mask qsfp-monitor.service >/dev/null + fi +fi + +if [ "$1" = "purge" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper purge s9100-monitor.service >/dev/null + deb-systemd-helper unmask s9100-monitor.service >/dev/null + deb-systemd-helper purge qsfp-monitor.service >/dev/null + deb-systemd-helper unmask qsfp-monitor.service >/dev/null + fi +fi +# Generate kernel modules.dep and map files for remove eeprom_mb. +depmod -a || true +# End automatically added section + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.prerm b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.prerm new file mode 100644 index 000000000000..2ca8a83b273f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.prerm @@ -0,0 +1,17 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + deb-systemd-invoke stop s9100-monitor.service >/dev/null + deb-systemd-invoke stop qsfp-monitor.service >/dev/null +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s9100-monitor" ]; then + invoke-rc.d s9100-monitor stop || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + invoke-rc.d qsfp-monitor stop || exit $? +fi +# Driver deinit +/usr/sbin/i2c_utils.sh i2c_deinit +# End automatically added section + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.upstart b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.upstart new file mode 100644 index 000000000000..5d0ba7080129 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9100.upstart @@ -0,0 +1,6 @@ +description "SONiC platform service" + +respawn + +exec /usr/sbin/s9100_monitor.sh +exec /usr/sbin/qsfp_monitor.sh diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.dirs b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.dirs new file mode 100644 index 000000000000..bec29f195770 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.dirs @@ -0,0 +1,3 @@ +usr/sbin +lib/systemd/system +etc/ \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.install b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.install new file mode 100644 index 000000000000..38efbef5d864 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.install @@ -0,0 +1,3 @@ +lib/systemd/ +usr/sbin/ +etc/ \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.postinst b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.postinst new file mode 100644 index 000000000000..0605612a1190 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.postinst @@ -0,0 +1,43 @@ +# Automatically added by dh_systemd_enable +# This will only remove masks created by d-s-h on package removal. +deb-systemd-helper unmask s9200-64x-monitor.service >/dev/null || true +deb-systemd-helper unmask qsfp-monitor.service >/dev/null || true +# Generate kernel modules.dep and map files for add eeprom_mb. +depmod -a || true +# was-enabled defaults to true, so new installations run enable. +if deb-systemd-helper --quiet was-enabled s9200-64x-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable s9200-64x-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state s9200-64x-monitor.service >/dev/null || true +fi +if deb-systemd-helper --quiet was-enabled qsfp-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable qsfp-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state qsfp-monitor.service >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s9200-64x-monitor" ]; then + update-rc.d s9200-64x-monitor defaults >/dev/null + invoke-rc.d s9200-64x-monitor start || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + update-rc.d qsfp-monitor defaults >/dev/null + invoke-rc.d qsfp-monitor start || exit $? +fi +# End automatically added section +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + deb-systemd-invoke start s9200-64x-monitor.service >/dev/null || true + deb-systemd-invoke start qsfp-monitor.service >/dev/null || true +fi +# End automatically added section diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.postrm b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.postrm new file mode 100644 index 000000000000..3e9494097556 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.postrm @@ -0,0 +1,38 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + fi +# End automatically added section +# Automatically added by dh_installinit +if [ "$1" = "purge" ] ; then + update-rc.d s9200-64x-monitor remove >/dev/null + update-rc.d qsfp-monitor remove >/dev/null +fi + + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [ -d /run/systemd/system ] ; then + systemctl --system daemon-reload >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_systemd_enable +if [ "$1" = "remove" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper mask s9200-64x-monitor.service >/dev/null + deb-systemd-helper mask qsfp-monitor.service >/dev/null + fi +fi + +if [ "$1" = "purge" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper purge s9200-64x-monitor.service >/dev/null + deb-systemd-helper unmask s9200-64x-monitor.service >/dev/null + deb-systemd-helper purge qsfp-monitor.service >/dev/null + deb-systemd-helper unmask qsfp-monitor.service >/dev/null + fi +fi +# Generate kernel modules.dep and map files for remove eeprom_mb. +depmod -a || true +# End automatically added section + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.prerm b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.prerm new file mode 100644 index 000000000000..6238dcad7886 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.prerm @@ -0,0 +1,17 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + deb-systemd-invoke stop s9200-64x-monitor.service >/dev/null + deb-systemd-invoke stop qsfp-monitor.service >/dev/null +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s9200-64x-monitor" ]; then + invoke-rc.d s9200-64x-monitor stop || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + invoke-rc.d qsfp-monitor stop || exit $? +fi +# Driver deinit +/usr/sbin/i2c_utils.sh i2c_deinit +# End automatically added section + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.upstart b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.upstart new file mode 100644 index 000000000000..00803ff5e3b9 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9200-64x.upstart @@ -0,0 +1,6 @@ +description "SONiC platform service" + +respawn + +exec /usr/sbin/s9200_64x_monitor.sh +exec /usr/sbin/qsfp_monitor.sh diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/README.md b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/README.md new file mode 100644 index 000000000000..dec38cb33393 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/README.md @@ -0,0 +1,185 @@ +# Ingrasys S8810-32Q Platform Driver for SONiC + +Copyright (C) 2016 Ingrasys, Inc. + +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 3 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, see . + + +## Licensing terms + +The Licensing terms of the files within this project is split 2 parts. +The Linux kernel is released under the GNU General Public License version 2. +All other code is released under the GNU General Public License version 3. +Please see the LICENSE file for copies of both licenses. + +## Contents of this package + + - service/ + > Service config files for platform initialization and monitoring + - utils/ + > Scripts useful during platform bringup and sysfs function + - conf/ + > Platform configure files. + +## Kernel modules and drivers + +The driver for interacting with Ingrasys S8810-32Q is contained in the I2C +kernel module and initialization script. The initialization script loads +the modules in the correct order. It has been built and tested against +the Linux 3.16 kernel. + +The initialization script will modprobe the needed modules, navigate to the +module's device directory in sysfs, and write configuration data to +the kernel module. + +### IGB + +This is OOB Ports on front panel for management plane. + +The IGB module must be loaded first on Ingrasys S8810-32Q platform. + +### I2C i801 + +The I2C i801 on Ingrasys S8810-32Q can be found in +`/sys/bus/i2c/devices/i2c-0/` + +This is I2C bus for Clock Gen, DIMM channel and digital potentiometers. + +The i801 module must be loaded second on Ingrasys S8810-32Q. + +### I2C iSMT + +The I2C iSMT module on S8810-32Q can be found in +`/sys/bus/i2c/devices/i2c-1/` + +This is I2C bus for CPLD, HWM, power controller and I2C Switches. + +The i801 module must be loaded third on Ingrasys S8810-32Q. + +### I2C PCA9548 +The PCA9548 module on S8810-32Q can be found in +`/sys/bus/i2c/devices/i2c-2/` , `/sys/bus/i2c/devices/i2c-3/`, +`/sys/bus/i2c/devices/i2c-4/`, `/sys/bus/i2c/devices/i2c-5/`, +`/sys/bus/i2c/devices/i2c-6/`, `/sys/bus/i2c/devices/i2c-7/`, +`/sys/bus/i2c/devices/i2c-8/`, `/sys/bus/i2c/devices/i2c-9/`. + +The pca9548 module for zQSFP module get/set functions, PSU information, +fan status and EEPROM. + +## Hardware components + +The hardware components are initialized in the init script on S8810-32Q. +The following describes manual initialization as well as interaction. +The examples below are just for Ingrasys S8810-32Q platform. + +### Hardware initialization + +When the sonic-platform-ingrasys-s8810-32q package is installed on the S8810-32Q, +it is automatically initialized. If you want to manual initialization, the +utility command usage as follows: +``` + i2c_utils.sh i2c_init +``` + +### EEPROM + +The EEPROM is including the board SKU, model name, vendor name, serial number, +and other information can be accessed with the specific eeprom kernel module. +After using `modprobe eeprom_mb` to detect the eeprom, it will show up in sysfs. + +The hexdump utility can be used to decode the raw output of the EEPROM. +For example, +``` + bash# echo "mb_eeprom 0x54" > /sys/bus/i2c/devices/i2c-9/new_device + bash# cat /sys/bus/i2c/drivers/mb_eeprom/9-0054/eeprom | hexdump -C +``` + +### Front panel LEDs + +LEDs can be setup on/off by using i2c utility `/usr/sbin/i2c_utils.sh`. +Utility function command usage as follows: + +``` +Status LED: + i2c_utils.sh i2c_sys_led green|amber on|off + +Fan status LED: + i2c_utils.sh i2c_fan_led green|amber on|off + +PSU1 status LED: + i2c_utils.sh i2c_psu1_led green|amber on|off + +PSU2 status LED: + i2c_utils.sh i2c_psu2_led green|amber on|off + +``` +QSFP Module Port LEDs control by ASIC library. + + +### Fan speed + +Fan speed are controlled by the w83795 kernel module. +It can be found in `/sys/class/hwmon/hwmon1/device/`. +If they were compiled as modules, you will need to modprobe w83795 for +their sysfs entries to show up. Each fan has an `fan_input` file +for reading the fan speed. And `pwm1` setting fan1 to fan4, +`pwm2` setting fan5 to fan8. + +There is docker-platform-monitor container installed fancontrol package that can +automatic control platform fan speed. + + +### Temperature sensors + +Temperature sensors are controlled by the w83795 kernel +module. It can be found in `/sys/class/hwmon/hwmon1/device/`. +If they were compiled as modules, then you will need to modprobe w83795 for +their sysfs entries to show up. +`temp1_input` is front MAC temperature sensor. `temp2_input` is rear MAC +temperature sensor. + +There is docker-platform-monitor container installed lm-sensors package that can +monitor platform temperature. And `sensors` command can show each +temperature sensors status. + +### Power supplies + +Power supplies status and its EEPROM info can be used i2c utility +`/usr/sbin/i2c_utils.sh` to get. +The usage as follows: +``` +PSU EEPROM: + i2c_utils.sh i2c_psu_eeprom_get + hexdump -C psu0.rom + hexdump -C psu1.rom + +PSU Status: + i2c_utils.sh i2c_psu_status +``` + +### QSFPs +QSFP modules are managed by the pca9548 kernel driver. +The i2c utility `/usr/sbin/i2c_utils.sh` can be used to get status and +module EEPROM informations. +The usage as follows: +``` +QSFP EEPROM: + i2c_utils.sh i2c_qsfp_eeprom_get [1-32] + +QSFP Insert Event: + i2c_utils.sh i2c_qsfp_eeprom_get [1-32] + 0 => not insert + 1 => inserted +``` + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/modules/Makefile b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/modules/Makefile new file mode 100755 index 000000000000..7c782163f402 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/modules/Makefile @@ -0,0 +1,2 @@ +obj-m := eeprom_mb.o +obj-m += ingrasys_s8810_32q_psu.o diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/modules/eeprom_mb.c b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/modules/eeprom_mb.c new file mode 100755 index 000000000000..2040e3cfc5db --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/modules/eeprom_mb.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 1998, 1999 Frodo Looijaard and + * Philip Edelbrock + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003 IBM Corp. + * Copyright (C) 2004 Jean Delvare + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, I2C_CLIENT_END }; + + +/* Size of EEPROM in bytes */ +#define EEPROM_SIZE 256 + +#define SLICE_BITS (6) +#define SLICE_SIZE (1 << SLICE_BITS) +#define SLICE_NUM (EEPROM_SIZE/SLICE_SIZE) + +/* Each client has this additional data */ +struct eeprom_data { + struct mutex update_lock; + u8 valid; /* bitfield, bit!=0 if slice is valid */ + unsigned long last_updated[SLICE_NUM]; /* In jiffies, 8 slices */ + u8 data[EEPROM_SIZE]; /* Register values */ +}; + + +static void mb_eeprom_update_client(struct i2c_client *client, u8 slice) +{ + struct eeprom_data *data = i2c_get_clientdata(client); + int i, j; + int ret; + int addr; + + mutex_lock(&data->update_lock); + + if (!(data->valid & (1 << slice)) || + time_after(jiffies, data->last_updated[slice] + 300 * HZ)) { + dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice); + + addr = slice << SLICE_BITS; + + ret = i2c_smbus_write_byte_data(client, (u8)((addr >> 8) & 0xFF), (u8)(addr & 0xFF)); + /* select the eeprom address */ + if (ret < 0) { + dev_err(&client->dev, "address set failed\n"); + goto exit; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE)) { + goto exit; + } + + for (i = slice << SLICE_BITS; i < (slice + 1) << SLICE_BITS; i+= SLICE_SIZE) { + for (j = i; j < (i+SLICE_SIZE); j++) { + int res; + + res = i2c_smbus_read_byte(client); + if (res < 0) { + goto exit; + } + + data->data[j] = res & 0xFF; + } + } + + data->last_updated[slice] = jiffies; + data->valid |= (1 << slice); + } +exit: + mutex_unlock(&data->update_lock); +} + +static ssize_t mb_eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct eeprom_data *data = i2c_get_clientdata(client); + u8 slice; + + if (off > EEPROM_SIZE) { + return 0; + } + if (off + count > EEPROM_SIZE) { + count = EEPROM_SIZE - off; + } + if (count == 0) { + return 0; + } + + /* Only refresh slices which contain requested bytes */ + for (slice = off >> SLICE_BITS; slice <= (off + count - 1) >> SLICE_BITS; slice++) { + mb_eeprom_update_client(client, slice); + } + + memcpy(buf, &data->data[off], count); + + return count; +} + +static struct bin_attribute mb_eeprom_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO, + }, + .size = EEPROM_SIZE, + .read = mb_eeprom_read, +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int mb_eeprom_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all + addresses 0x50-0x57, but we only care about 0x50. So decline + attaching to addresses >= 0x51 on DDC buses */ + if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x56) { + return -ENODEV; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE) + && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + return -ENODEV; + } + + strlcpy(info->type, "eeprom", I2C_NAME_SIZE); + + return 0; +} + +static int mb_eeprom_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct eeprom_data *data; + int err; + + if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + memset(data->data, 0xff, EEPROM_SIZE); + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &mb_eeprom_attr); + if (err) { + goto exit_kfree; + } + + return 0; + +exit_kfree: + kfree(data); +exit: + return err; +} + +static int mb_eeprom_remove(struct i2c_client *client) +{ + sysfs_remove_bin_file(&client->dev.kobj, &mb_eeprom_attr); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id mb_eeprom_id[] = { + { "mb_eeprom", 0 }, + { } +}; + +static struct i2c_driver mb_eeprom_driver = { + .driver = { + .name = "mb_eeprom", + }, + .probe = mb_eeprom_probe, + .remove = mb_eeprom_remove, + .id_table = mb_eeprom_id, + + .class = I2C_CLASS_DDC | I2C_CLASS_SPD, + .detect = mb_eeprom_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(mb_eeprom_driver); + +MODULE_AUTHOR("Wade "); +MODULE_DESCRIPTION("Ingrasys S8810-32Q Mother Board EEPROM driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/modules/ingrasys_s8810_32q_platform.h b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/modules/ingrasys_s8810_32q_platform.h new file mode 100644 index 000000000000..a88755946e9a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/modules/ingrasys_s8810_32q_platform.h @@ -0,0 +1,148 @@ +#ifndef _S8810_32Q_PLATFORM_H +#define _S8810_32Q_PLATFORM_H + +#include + +// remove debug before release +#define DEBUG + +enum bus_order { + I2C_BUS_MAIN, + MUX_9548_0_CH0, + MUX_9548_0_CH1, + MUX_9548_0_CH2, + MUX_9548_0_CH3, + MUX_9548_0_CH4, + MUX_9548_0_CH5, + MUX_9548_0_CH6, + MUX_9548_0_CH7, + MUX_9548_1_CH0, + MUX_9548_1_CH1, + MUX_9548_1_CH2, + MUX_9548_1_CH3, + MUX_9548_1_CH4, + MUX_9548_1_CH5, + MUX_9548_1_CH6, + MUX_9548_1_CH7, + MUX_9546_0_CH0, + MUX_9546_0_CH1, + MUX_9546_0_CH2, + MUX_9546_0_CH3, + MUX_9546_1_CH0, + MUX_9546_1_CH1, + MUX_9546_1_CH2, + MUX_9546_1_CH3, + MUX_9548_2_CH0, + MUX_9548_2_CH1, + MUX_9548_2_CH2, + MUX_9548_2_CH3, + MUX_9548_2_CH4, + MUX_9548_2_CH5, + MUX_9548_2_CH6, + MUX_9548_2_CH7, + MUX_9548_3_CH0, + MUX_9548_3_CH1, + MUX_9548_3_CH2, + MUX_9548_3_CH3, + MUX_9548_3_CH4, + MUX_9548_3_CH5, + MUX_9548_3_CH6, + MUX_9548_3_CH7, + MUX_9548_4_CH0, + MUX_9548_4_CH1, + MUX_9548_4_CH2, + MUX_9548_4_CH3, + MUX_9548_4_CH4, + MUX_9548_4_CH5, + MUX_9548_4_CH6, + MUX_9548_4_CH7, + MUX_9548_5_CH0, + MUX_9548_5_CH1, + MUX_9548_5_CH2, + MUX_9548_5_CH3, + MUX_9548_5_CH4, + MUX_9548_5_CH5, + MUX_9548_5_CH6, + MUX_9548_5_CH7, + MUX_9548_6_CH0, + MUX_9548_6_CH1, + MUX_9548_6_CH2, + MUX_9548_6_CH3, + MUX_9548_6_CH4, + MUX_9548_6_CH5, + MUX_9548_6_CH6, + MUX_9548_6_CH7, + MUX_9548_7_CH0, + MUX_9548_7_CH1, + MUX_9548_7_CH2, + MUX_9548_7_CH3, + MUX_9548_7_CH4, + MUX_9548_7_CH5, + MUX_9548_7_CH6, + MUX_9548_7_CH7, + MUX_9548_8_CH0, + MUX_9548_8_CH1, + MUX_9548_8_CH2, + MUX_9548_8_CH3, + MUX_9548_8_CH4, + MUX_9548_8_CH5, + MUX_9548_8_CH6, + MUX_9548_8_CH7, + MUX_9548_9_CH0, + MUX_9548_9_CH1, + MUX_9548_9_CH2, + MUX_9548_9_CH3, + MUX_9548_9_CH4, + MUX_9548_9_CH5, + MUX_9548_9_CH6, + MUX_9548_9_CH7, + MUX_9548_10_CH0, + MUX_9548_10_CH1, + MUX_9548_10_CH2, + MUX_9548_10_CH3, + MUX_9548_10_CH4, + MUX_9548_10_CH5, + MUX_9548_10_CH6, + MUX_9548_10_CH7, +}; + +#define I2C_ADDR_MUX_9555_0 (0x20) +#define I2C_ADDR_MUX_9555_1 (0x24) +#define I2C_ADDR_MUX_9555_2 (0x25) +#define I2C_ADDR_MUX_9555_3 (0x26) +#define I2C_ADDR_MUX_9539_0 (0x76) +#define I2C_ADDR_MUX_9539_1 (0x76) +#define I2C_BUS_FAN_STATUS (I2C_BUS_MAIN) +#define I2C_BUS_SYS_LED (MUX_9548_1_CH1) + +#define NUM_OF_I2C_MUX (11) +#define NUM_OF_CPLD (5) +#define NUM_OF_QSFP_PORT (64) +#define NUM_OF_SFP_PORT (2) +#define QSFP_EEPROM_I2C_ADDR (0x50) + +enum gpio_reg { + REG_PORT0_IN, + REG_PORT1_IN, + REG_PORT0_OUT, + REG_PORT1_OUT, + REG_PORT0_POL, + REG_PORT1_POL, + REG_PORT0_DIR, + REG_PORT1_DIR, +}; + +struct ing_i2c_board_info { + int ch; + int size; + struct i2c_board_info *board_info; +}; + +struct i2c_init_data { + __u16 ch; + __u16 addr; + __u8 reg; + __u8 value; +}; + +#endif diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/modules/ingrasys_s8810_32q_psu.c b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/modules/ingrasys_s8810_32q_psu.c new file mode 100644 index 000000000000..ae34c8eb33ed --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/modules/ingrasys_s8810_32q_psu.c @@ -0,0 +1,393 @@ +/* + * S8810-32Q PSU driver + * + * Copyright (C) 2017 Ingrasys, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ingrasys_s8810_32q_platform.h" + +static ssize_t show_psu_eeprom(struct device *dev, + struct device_attribute *da, + char *buf); +static struct s8810_psu_data *s8810_psu_update_status(struct device *dev); +static struct s8810_psu_data *s8810_psu_update_eeprom(struct device *dev); +static int s8810_psu_read_block(struct i2c_client *client, + u8 command, + u8 *data, + int data_len); + + +#define DRIVER_NAME "psu" + +// Addresses scanned +static const unsigned short normal_i2c[] = { 0x51, I2C_CLIENT_END }; + +/* PSU EEPROM SIZE */ +#define EEPROM_SZ 256 +#define READ_EEPROM 1 +#define NREAD_EEPROM 0 + +static struct i2c_client cpld_client; + +/* CPLD Registers */ +#define REG_PSU_PG 0x02 +#define REG_PSU_PRSNT 0x03 + +/* CPLD Pins */ +#define PSU1_PRSNT_L 0 +#define PSU2_PRSNT_L 1 +#define PSU1_PWROK 3 +#define PSU2_PWROK 4 + + +/* Driver Private Data */ +struct s8810_psu_data { + struct mutex lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + char eeprom[EEPROM_SZ]; /* psu eeprom data */ + char psuABS; /* PSU absent */ + char psuPG; /* PSU power good */ +}; + +enum psu_index +{ + s8810_psu1, + s8810_psu2 +}; + +/* + * display power good attribute + */ +static ssize_t +show_psu_pg(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct s8810_psu_data *data = s8810_psu_update_status(dev); + unsigned int value; + + mutex_lock(&data->lock); + value = data->psuPG; + mutex_unlock(&data->lock); + + return sprintf(buf, "%d\n", value); +} + +/* + * display power absent attribute + */ +static ssize_t +show_psu_abs(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct s8810_psu_data *data = s8810_psu_update_status(dev); + unsigned int value; + + mutex_lock(&data->lock); + value = data->psuABS; + mutex_unlock(&data->lock); + + return sprintf(buf, "%d\n", value); +} + + +/* + * sysfs attributes for psu + */ +static DEVICE_ATTR(psu_pg, S_IRUGO, show_psu_pg, NULL); +static DEVICE_ATTR(psu_abs, S_IRUGO, show_psu_abs, NULL); +static DEVICE_ATTR(psu_eeprom, S_IRUGO, show_psu_eeprom, NULL); + +static struct attribute *s8810_psu_attributes[] = { + &dev_attr_psu_pg.attr, + &dev_attr_psu_abs.attr, + &dev_attr_psu_eeprom.attr, + NULL +}; + +/* + * display psu eeprom content + */ +static ssize_t +show_psu_eeprom(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct s8810_psu_data *data = s8810_psu_update_eeprom(dev); + + memcpy(buf, (char *)data->eeprom, EEPROM_SZ); + return EEPROM_SZ; +} + +static const struct attribute_group s8810_psu_group = { + .attrs = s8810_psu_attributes, +}; + +/* + * check gpio expander is accessible + */ +static int +cpld_detect(struct i2c_client *client) +{ + if (i2c_smbus_read_byte_data(client, REG_PSU_PG) < 0) { + return -ENODEV; + } + + return 0; +} + +/* + * client address init + */ +static void +i2c_devices_client_address_init(struct i2c_client *client) +{ + cpld_client = *client; + cpld_client.addr = 0x33; +} + +static int +s8810_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct s8810_psu_data *data; + int status, err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct s8810_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct s8810_psu_data)); + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->lock); + + i2c_devices_client_address_init(client); + + err = cpld_detect(&cpld_client); + if (err) { + dev_info(&client->dev, "cpld detect failure\n"); + return err; + } + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &s8810_psu_group); + if (status) { + goto exit_free; + } + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &s8810_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int +s8810_psu_remove(struct i2c_client *client) +{ + struct s8810_psu_data *data = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &s8810_psu_group); + kfree(data); + + return 0; +} + + +/* + * psu eeprom read utility + */ +static int +s8810_psu_read_block(struct i2c_client *client, + u8 command, + u8 *data, + int data_len) +{ + int i=0, ret=0; + int blk_max = 32; //max block read size + + /* read eeprom, 32 * 8 = 256 bytes */ + for (i=0; i < EEPROM_SZ/blk_max; i++) { + ret = i2c_smbus_read_i2c_block_data(client, (i*blk_max), blk_max, + data + (i*blk_max)); + if (ret < 0) { + return ret; + } + } + return ret; +} + +/* + * update eeprom content + */ +static struct s8810_psu_data +*s8810_psu_update_eeprom(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s8810_psu_data *data = i2c_get_clientdata(client); + s32 status = 0; + char psu_pg = 0; + char psu_prsnt = 0; + int psu_pwrok = 0; + int psu_prsnt_l = 0; + + mutex_lock(&data->lock); + + if (time_after(jiffies, data->last_updated + 300 * HZ) + || !data->valid) { + + /* Read psu status */ + + psu_pg = i2c_smbus_read_byte_data(&(cpld_client), REG_PSU_PG); + psu_prsnt = i2c_smbus_read_byte_data(&(cpld_client), REG_PSU_PRSNT); + + /*read psu status from io expander*/ + + if (data->index == s8810_psu1) { + psu_pwrok = PSU1_PWROK; + psu_prsnt_l = PSU1_PRSNT_L; + } else { + psu_pwrok = PSU2_PWROK; + psu_prsnt_l = PSU2_PRSNT_L; + } + data->psuPG = (psu_pg >> psu_pwrok) & 0x1; + data->psuABS = (psu_prsnt >> psu_prsnt_l) & 0x1; + + /* Read eeprom */ + if (!data->psuABS) { + //clear local eeprom data + memset(data->eeprom, 0, EEPROM_SZ); + + //read eeprom + status = s8810_psu_read_block(client, 0, data->eeprom, + ARRAY_SIZE(data->eeprom)); + + if (status < 0) { + memset(data->eeprom, 0, EEPROM_SZ); + dev_err(&client->dev, "Read eeprom failed, status=(%d)\n", status); + } else { + data->valid = 1; + } + } else { + memset(data->eeprom, 0, EEPROM_SZ); + } + data->last_updated = jiffies; + } + + mutex_unlock(&data->lock); + + return data; +} + +/* + * update psu status + */ +static struct s8810_psu_data +*s8810_psu_update_status(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s8810_psu_data *data = i2c_get_clientdata(client); + char psu_pg = 0; + char psu_prsnt = 0; + int psu_pwrok = 0; + int psu_prsnt_l = 0; + + mutex_lock(&data->lock); + + /* Read psu status */ + + psu_pg = i2c_smbus_read_byte_data(&(cpld_client), REG_PSU_PG); + psu_prsnt = i2c_smbus_read_byte_data(&(cpld_client), REG_PSU_PRSNT); + + /*read psu status from io expander*/ + + if (data->index == s8810_psu1) { + psu_pwrok = PSU1_PWROK; + psu_prsnt_l = PSU1_PRSNT_L; + } else { + psu_pwrok = PSU2_PWROK; + psu_prsnt_l = PSU2_PRSNT_L; + } + data->psuPG = (psu_pg >> psu_pwrok) & 0x1; + data->psuABS = (psu_prsnt >> psu_prsnt_l) & 0x1; + + mutex_unlock(&data->lock); + + return data; +} + +static const struct i2c_device_id s8810_psu_id[] = { + { "psu1", s8810_psu1 }, + { "psu2", s8810_psu2 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, s8810_psu_id); + +static struct i2c_driver s8810_psu_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = s8810_psu_probe, + .remove = s8810_psu_remove, + .id_table = s8810_psu_id, + .address_list = normal_i2c, +}; + +static int __init s8810_psu_init(void) +{ + return i2c_add_driver(&s8810_psu_driver); +} + +static void __exit s8810_psu_exit(void) +{ + i2c_del_driver(&s8810_psu_driver); +} + +module_init(s8810_psu_init); +module_exit(s8810_psu_exit); + +MODULE_AUTHOR("Jason Tsai "); +MODULE_DESCRIPTION("S8810-32Q psu driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/service/qsfp-monitor.service b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/service/qsfp-monitor.service new file mode 100644 index 000000000000..3d69fdae0f87 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/service/qsfp-monitor.service @@ -0,0 +1,15 @@ +[Unit] +Description= This QSFP Monitor service is to setup QSFP SI. +Requires=s8810-32q-monitor.service +After=s8810-32q-monitor.service + +[Service] +ExecStart=/usr/sbin/qsfp_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/service/s8810-32q-monitor.service b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/service/s8810-32q-monitor.service new file mode 100644 index 000000000000..644efe443256 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/service/s8810-32q-monitor.service @@ -0,0 +1,19 @@ +[Unit] +Description= This Platform Monitor service is to initialize platform and monitor platform. +Before=platform-monitor.service +After=sysinit.target +Wants=fancontrol.service +Wants=qsfp-monitor.service +DefaultDependencies=no + +[Service] +ExecStartPre=/usr/sbin/i2c_utils.sh i2c_init +ExecStart=/usr/sbin/s8810_32q_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/utils/i2c_utils.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/utils/i2c_utils.sh new file mode 100644 index 000000000000..98ffb9d8d2f4 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/utils/i2c_utils.sh @@ -0,0 +1,1452 @@ +#!/bin/bash + +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +VERSION="1.0.0" +TRUE=200 +FALSE=404 + +EXEC_FUNC=${1} +COLOR_LED=${2} +QSFP_PORT=${2} +QSFP_ACTION=${2} +MB_EEPROM_ACTION=${2} +ONOFF_LED=${3} +FAN_TRAY=${4} + +############################################################ +# Distributor ID: Debian +# Description: Debian GNU/Linux 8.6 (jessie) +# Release: 8.6 +# Codename: jessie +# Linux debian 3.16.0-4-amd64 #1 +# SMP Debian 3.16.36-1+deb8u1 (2016-09-03) x86_64 GNU/Linux +############################################################ + +# Color Definition +COLOR_TITLE="\e[1;32m" ### Green ### +COLOR_WARNING="\e[1;33m" ### Yellow ### +COLOR_ERROR="\e[1;31m" ### Red ### +COLOR_END="\e[0m" ### END ### + +NUM_I801_DEVICE=0 +NUM_ISMT_DEVICE=$(( ${NUM_I801_DEVICE} + 1 )) + +#MUX PCA9548#0 +NUM_MUX_9548_0_CHAN0=$(( ${NUM_I801_DEVICE} + 2 )) +NUM_MUX_9548_0_CHAN1=$(( ${NUM_I801_DEVICE} + 3 )) +NUM_MUX_9548_0_CHAN2=$(( ${NUM_I801_DEVICE} + 4 )) +#NUM_MUX_9548_0_CHAN3=$(( ${NUM_I801_DEVICE} + 5 )) +#NUM_MUX_9548_0_CHAN4=$(( ${NUM_I801_DEVICE} + 6 )) +#NUM_MUX_9548_0_CHAN5=$(( ${NUM_I801_DEVICE} + 7 )) +#NUM_MUX_9548_0_CHAN6=$(( ${NUM_I801_DEVICE} + 8 )) +#NUM_MUX_9548_0_CHAN7=$(( ${NUM_I801_DEVICE} + 9 )) + +#MUX PCA9548#1 +NUM_MUX_9548_1_CHAN0=$(( ${NUM_I801_DEVICE} + 10 )) +NUM_MUX_9548_1_CHAN1=$(( ${NUM_I801_DEVICE} + 11 )) +NUM_MUX_9548_1_CHAN2=$(( ${NUM_I801_DEVICE} + 12 )) +NUM_MUX_9548_1_CHAN3=$(( ${NUM_I801_DEVICE} + 13 )) +NUM_MUX_9548_1_CHAN4=$(( ${NUM_I801_DEVICE} + 14 )) +NUM_MUX_9548_1_CHAN5=$(( ${NUM_I801_DEVICE} + 15 )) +NUM_MUX_9548_1_CHAN6=$(( ${NUM_I801_DEVICE} + 16 )) +NUM_MUX_9548_1_CHAN7=$(( ${NUM_I801_DEVICE} + 17 )) + +NUM_MUX_9548_2_CHAN0=$(( ${NUM_I801_DEVICE} + 18 )) +NUM_MUX_9548_3_CHAN0=$(( ${NUM_I801_DEVICE} + 26 )) +NUM_MUX_9548_4_CHAN0=$(( ${NUM_I801_DEVICE} + 34 )) +NUM_MUX_9548_5_CHAN0=$(( ${NUM_I801_DEVICE} + 42 )) + +#MUX Alias +I2C_BUS_PSU1_EEPROM=${NUM_MUX_9548_0_CHAN1} +I2C_BUS_PSU2_EEPROM=${NUM_MUX_9548_0_CHAN2} +I2C_BUS_FAN_STATUS=${NUM_MUX_9548_1_CHAN0} +I2C_BUS_LED_BOARD=${NUM_MUX_9548_1_CHAN0} +I2C_BUS_MB_EEPROM=${NUM_MUX_9548_1_CHAN2} + +PATH_SYS_I2C_DEVICES="/sys/bus/i2c/devices" +PATH_SYS_GPIO="/sys/class/gpio" +PATH_HWMON_ROOT_DEVICES="/sys/class/hwmon" +PATH_HWMON_W83795_DEVICE="${PATH_HWMON_ROOT_DEVICES}/hwmon1" +PATH_I801_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_I801_DEVICE}" +PATH_ISMT_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_ISMT_DEVICE}" +#PATH for MUX PCA9548#0 +PATH_MUX_9548_0_CHAN0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CHAN0}" +PATH_MUX_9548_0_CHAN1="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CHAN1}" +PATH_MUX_9548_0_CHAN2="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CHAN2}" +#PATH for MUX PCA9548#1 +PATH_MUX_9548_1_CHAN0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN0}" +PATH_MUX_9548_1_CHAN1="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN1}" +PATH_MUX_9548_1_CHAN2="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN2}" +PATH_MUX_9548_1_CHAN3="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN3}" +PATH_MUX_9548_1_CHAN4="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN4}" +PATH_MUX_9548_1_CHAN5="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN5}" +PATH_MUX_9548_1_CHAN6="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN6}" +PATH_MUX_9548_1_CHAN7="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN7}" + +#i2cmap and gpiomap path +PATH_GPIOMAP=/tmp/gpiomap +PATH_I2CMAP=/tmp/i2cmap + +I2C_ADDR_MUX_9548_0=0x70 +I2C_ADDR_MUX_9548_1=0x70 +I2C_ADDR_MUX_9548_2=0x71 +I2C_ADDR_MUX_9548_3=0x71 +I2C_ADDR_MUX_9548_4=0x71 +I2C_ADDR_MUX_9548_5=0x71 +I2C_ADDR_DDR3=0x50 +I2C_ADDR_RTC=0x68 +I2C_ADDR_CPLD=0x33 +I2C_ADDR_HWM=0x2F +I2C_ADDR_MUX_9535_FAN=0x20 +I2C_ADDR_MUX_9535_LED=0x22 +I2C_ADDR_MUX_9535_0=0x20 +I2C_ADDR_MUX_9535_1=0x21 +I2C_ADDR_MUX_9535_2=0x22 +I2C_ADDR_MUX_9535_3=0x23 +I2C_ADDR_MUX_9535_4=0x20 +I2C_ADDR_MUX_9535_5=0x21 +I2C_ADDR_MUX_9535_6=0x22 +I2C_ADDR_MUX_9535_7=0x23 +I2C_ADDR_MUX_9535_8=0x20 +I2C_ADDR_MUX_9535_9=0x21 +I2C_ADDR_MUX_9535_10=0x22 +I2C_ADDR_MB_EEPROM=0x56 +I2C_ADDR_QSFP_EEPROM=0x50 +I2C_ADDR_PSU_EEPROM=0x51 + +#sysfs +PATH_SYSFS_PSU1="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_PSU1_EEPROM}-$(printf "%04x" $I2C_ADDR_PSU_EEPROM)" +PATH_SYSFS_PSU2="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_PSU2_EEPROM}-$(printf "%04x" $I2C_ADDR_PSU_EEPROM)" + +#Active High/Low +ACTIVE_LOW=1 +ACTIVE_HIGH=0 +#GPIO Direction In/Out +DIR_IN=in +DIR_OUT=out + +#Power Supply Status +PSU_DC_ON=1 +PSU_DC_OFF=0 +PSU_EXIST=1 +PSU_NOT_EXIST=0 + +#W83795 Registers +REG_BANK_SEL=0x00 +REG_VOLT_CTRL1=0x02 +REG_VOLT_CTRL2=0x03 +REG_TEMP_CTRL1=0x04 +REG_TEMP_CTRL2=0x05 + +#PCA9535 Registers +REG_IN_0=0 +REG_IN_1=1 +REG_OUT_0=2 +REG_OUT_1=3 +REG_POLARITY_0=4 +REG_POLARITY_1=5 +REG_CFG_0=6 +REG_CFG_1=7 + +#Bit Mask +BIT_MASK=(1 2 4 8 16 32 64 128) + + +# Help usage function +function _help { + echo "=========================================================" + echo "# Description: Help Function" + echo "=========================================================" + echo "----------------------------------------------------" + echo "EX : ${0} help" + echo " : ${0} i2c_init" + echo " : ${0} i2c_deinit" + echo " : ${0} i2c_temp_init" + echo " : ${0} i2c_fan_init" + echo " : ${0} i2c_volmon_init" + echo " : ${0} i2c_io_exp_init" + echo " : ${0} i2c_gpio_init" + echo " : ${0} i2c_gpio_deinit" + echo " : ${0} i2c_psu_eeprom_get" + echo " : ${0} i2c_mb_eeprom_get" + echo " : ${0} i2c_qsfp_eeprom_get [1-32]" + echo " : ${0} i2c_qsfp_eeprom_init new|delete" + echo " : ${0} i2c_mb_eeprom_init new|delete" + echo " : ${0} i2c_qsfp_status_get [1-32]" + echo " : ${0} i2c_qsfp_type_get [1-32]" + echo " : ${0} i2c_board_type_get" + echo " : ${0} i2c_psu_status" + echo " : ${0} i2c_led_psu_status_set" + echo " : ${0} i2c_led_fan_status_set" + echo " : ${0} i2c_led_fan_tray_status_set" + echo " : ${0} i2c_led_fan_tray_test" + echo " : ${0} i2c_cpld_version" + echo " : ${0} i2c_test_all" + echo " : ${0} i2c_led_test" + echo " : ${0} i2c_sys_led green|amber on|off" + echo " : ${0} i2c_fan_led green|amber on|off" + echo " : ${0} i2c_psu1_led green|amber on|off" + echo " : ${0} i2c_psu2_led green|amber on|off" + echo " : ${0} i2c_fan_tray_led green|amber on|off [1-4]" + echo "----------------------------------------------------" +} + +#Pause function +function _pause { + read -p "$*" +} + +#Retry command function +function _retry { + local i + for i in {1..5}; + do + eval "${*}" && break || echo "retry"; sleep 1; + done +} + +#set i2cmap +function _set_i2cmap { + local i2c_n=$1 + local alias=$2 + + #create i2cmap dir if not exist + mkdir -p $PATH_I2CMAP + + #check i2c_n exists in sysfs + if [ ! -L ${PATH_SYS_I2C_DEVICES}/i2c-${i2c_n} ]; then + echo "${PATH_SYS_I2C_DEVICES}/i2c-${i2c_n} does not exist." + return + fi + + #create or update link + ln -sf ${PATH_SYS_I2C_DEVICES}/i2c-${i2c_n} ${PATH_I2CMAP}/${alias} +} + +#clear i2cmap +function _clear_i2cmap { + #delete i2cmap dir + rm -rf ${PATH_I2CMAP}/ +} + + +#I2C Init +function _i2c_init { + echo "=========================================================" + echo "# Description: I2C Init" + echo "=========================================================" + + rmmod i2c_ismt + rmmod i2c_i801 + modprobe i2c_i801 + modprobe i2c_ismt + modprobe i2c_dev + modprobe i2c_mux_pca954x force_deselect_on_exit=1 + + #add MUX PCA9548#0 on I801 + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CHAN0}" ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_0}' > ${PATH_I801_DEVICE}/new_device" + _set_i2cmap ${NUM_MUX_9548_0_CHAN0} "PCA9548_0" + else + echo "pca9548#0 ${I2C_ADDR_MUX_9548_0} already init." + fi + #add MUX PCA9548#1 on ISMT + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN0}" ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_1}' > ${PATH_ISMT_DEVICE}/new_device" + _set_i2cmap ${NUM_MUX_9548_1_CHAN0} "PCA9548_1" + else + echo "pca9548#1 ${I2C_ADDR_MUX_9548_1} already init." + fi + #add MUX PCA9548#2 on PCA9548#1 + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CHAN0}" ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_2}' > ${PATH_MUX_9548_1_CHAN4}/new_device" + _set_i2cmap ${NUM_MUX_9548_2_CHAN0} "PCA9548_2" + else + echo "pca9548#2 ${I2C_ADDR_MUX_9548_2} already init." + fi + #add MUX PCA9548#3 on PCA9548#1 + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_3_CHAN0}" ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_3}' > ${PATH_MUX_9548_1_CHAN5}/new_device" + _set_i2cmap ${NUM_MUX_9548_3_CHAN0} "PCA9548_3" + else + echo "pca9548#3 ${I2C_ADDR_MUX_9548_3} already init." + fi + #add MUX PCA9548#4 on PCA9548#1 + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_4_CHAN0}" ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_4}' > ${PATH_MUX_9548_1_CHAN6}/new_device" + _set_i2cmap ${NUM_MUX_9548_4_CHAN0} "PCA9548_4" + else + echo "pca9548#4 ${I2C_ADDR_MUX_9548_4} already init." + fi + #add MUX PCA9548#5 on PCA9548#1 + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_5_CHAN0}" ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_5}' > ${PATH_MUX_9548_1_CHAN7}/new_device" + _set_i2cmap ${NUM_MUX_9548_5_CHAN0} "PCA9548_5" + else + echo "pca9548#5 ${I2C_ADDR_MUX_9548_5} already init." + fi + #Init CPLD LED_CLR Register (Front Port LED) + i2cset -y ${NUM_I801_DEVICE} ${I2C_ADDR_CPLD} 0x34 0x10 + + rmmod coretemp + rmmod jc42 + rmmod w83795 + _i2c_temp_init + _i2c_volmon_init + modprobe coretemp + modprobe w83795 + modprobe jc42 + modprobe eeprom + modprobe eeprom_mb + modprobe gpio-pca953x + _i2c_fan_init + _i2c_io_exp_init + _i2c_gpio_init + _i2c_psu_init + _i2c_qsfp_eeprom_init "new" + _i2c_mb_eeprom_init "new" + _i2c_led_psu_status_set + _i2c_led_fan_status_set + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_sys_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_sys_led + _config_rmem +} + +#I2C Deinit +function _i2c_deinit { + _i2c_gpio_deinit + for mod in coretemp jc42 w83795 eeprom_mb gpio-pca953x i2c_mux_pca954x i2c_ismt i2c_i801 ingrasys_s8810_32q_psu; + do + [ "$(lsmod | grep "^$mod ")" != "" ] && rmmod $mod + done + _clear_i2cmap +} + +#Temperature sensor Init +function _i2c_temp_init { + echo -n "TEMP INIT..." + #select bank0 + i2cset -y -r ${NUM_I801_DEVICE} ${I2C_ADDR_HWM} ${REG_BANK_SEL} 0x80 + #enable TR1, TR2, TR3 temperature monitoring + i2cset -y -r ${NUM_I801_DEVICE} ${I2C_ADDR_HWM} ${REG_TEMP_CTRL2} 0x7F + + # CLKIN clock frequency set as 48Mhz + i2cset -y -r ${NUM_I801_DEVICE} ${I2C_ADDR_HWM} 0x01 0x1C +} + +#FAN Init +function _i2c_fan_init { + + local init_fan_speed=120 + echo -n "FAN INIT..." + if [ -e "${PATH_HWMON_W83795_DEVICE}" ]; then + #Init Fan Speed + echo $init_fan_speed > ${PATH_HWMON_W83795_DEVICE}/device/pwm1 + echo $init_fan_speed > ${PATH_HWMON_W83795_DEVICE}/device/pwm2 + echo "SUCCESS" + else + echo "FAIL, W83795 not found in path ${PATH_HWMON_W83795_DEVICE}" + fi +} + +#VOLMON Init +function _i2c_volmon_init { + #3.3V 3VDD + #VSEN1 ROV + #VSEN4 1V + #VSEN5 1.8V + #VSEN7 5V + echo -n "VOLMON INIT..." + #select bank0 + i2cset -y -r ${NUM_I801_DEVICE} ${I2C_ADDR_HWM} ${REG_BANK_SEL} 0x80 + #enable voltage monitoring VSEN1-8 + i2cset -y -r ${NUM_I801_DEVICE} ${I2C_ADDR_HWM} ${REG_VOLT_CTRL1} 0xFF + #enable voltage monitoring 3VDD and 3VBAT + i2cset -y -r ${NUM_I801_DEVICE} ${I2C_ADDR_HWM} ${REG_VOLT_CTRL2} 0x50 + #disable voltage monitoring VSEN12 and VSEN13 + i2cset -y -r ${NUM_I801_DEVICE} ${I2C_ADDR_HWM} ${REG_TEMP_CTRL1} 0x00 + echo "Done" +} + +#IO Expander Init +function _i2c_io_exp_init { + echo "=========================================================" + echo "# Description: I2C IO Expander Init" + echo "=========================================================" + + #SMBUS1 + + echo "Init FAN Status IO Expander" + #PCA9535_FAN FAN_STATUS + #command byte 2/3, output logic level is 0 + #command byte 4/5, polarity is not inverted + #command byte 6/7, direction is input + i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_FAN} ${REG_OUT_0} 0x11 + i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_FAN} ${REG_OUT_1} 0x11 + i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_FAN} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_FAN} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_FAN} ${REG_CFG_0} 0xCC + i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_FAN} ${REG_CFG_1} 0xCC + + echo "Init LED Status IO Expander" + #PCA9535_LED LED_BOARD + #command byte 2/3, output logic level is 0 + #command byte 4/5, polarity is not inverted + #command byte 6/7, direction is output + i2cset -y -r ${I2C_BUS_LED_BOARD} ${I2C_ADDR_MUX_9535_LED} ${REG_OUT_0} 0xFF #active low + i2cset -y -r ${I2C_BUS_LED_BOARD} ${I2C_ADDR_MUX_9535_LED} ${REG_OUT_1} 0xFF #active low + i2cset -y -r ${I2C_BUS_LED_BOARD} ${I2C_ADDR_MUX_9535_LED} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${I2C_BUS_LED_BOARD} ${I2C_ADDR_MUX_9535_LED} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${I2C_BUS_LED_BOARD} ${I2C_ADDR_MUX_9535_LED} ${REG_CFG_0} 0x00 + i2cset -y -r ${I2C_BUS_LED_BOARD} ${I2C_ADDR_MUX_9535_LED} ${REG_CFG_1} 0x00 + + #PCA9535#0-10 zQSFP + + echo "Init ZQSFP IO Expander" + + echo "set ZQSFP ABS" + #zQSFP 0-31 ABS + #command byte 4/5, polarity is not inverted + #command byte 6/7, direction is input + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_0} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_0} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_0} ${REG_CFG_0} 0xFF + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_0} ${REG_CFG_1} 0xFF + + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_1} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_1} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_1} ${REG_CFG_0} 0xFF + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_1} ${REG_CFG_1} 0xFF + + echo "set ZQSFP INT" + #zQSFP 0-31 INT + #command byte 4/5, polarity is not inverted + #command byte 6/7, direction is input + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_2} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_2} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_2} ${REG_CFG_0} 0xFF + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_2} ${REG_CFG_1} 0xFF + + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_3} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_3} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_3} ${REG_CFG_0} 0xFF + i2cset -y -r ${NUM_MUX_9548_1_CHAN1} ${I2C_ADDR_MUX_9535_3} ${REG_CFG_1} 0xFF + + echo "set ZQSFP LP_MODE" + #zQSFP 0-31 LP_MODE + #command byte 2/3, output logic level is 0 + #command byte 4/5, polarity is not inverted + #command byte 6/7, direction is output + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_4} ${REG_OUT_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_4} ${REG_OUT_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_4} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_4} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_4} ${REG_CFG_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_4} ${REG_CFG_1} 0x00 + + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_5} ${REG_OUT_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_5} ${REG_OUT_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_5} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_5} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_5} ${REG_CFG_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_5} ${REG_CFG_1} 0x00 + + echo "set ZQSFP RST" + #zQSFP 0-31 RST + #command byte 2/3, output logic level is 0 + #command byte 4/5, polarity is not inverted + #command byte 6/7, direction is output + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_6} ${REG_OUT_0} 0xFF + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_6} ${REG_OUT_1} 0xFF + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_6} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_6} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_6} ${REG_CFG_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_6} ${REG_CFG_1} 0x00 + + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_7} ${REG_OUT_0} 0xFF + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_7} ${REG_OUT_1} 0xFF + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_7} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_7} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_7} ${REG_CFG_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN2} ${I2C_ADDR_MUX_9535_7} ${REG_CFG_1} 0x00 + + echo "set ZQSFP Mode Select" + #zQSFP 0-31 Mode Select + #command byte 2/3, output logic level is 0 + #command byte 4/5, polarity is not inverted + #command byte 6/7, direction is output + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_8} ${REG_OUT_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_8} ${REG_OUT_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_8} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_8} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_8} ${REG_CFG_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_8} ${REG_CFG_1} 0x00 + + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_9} ${REG_OUT_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_9} ${REG_OUT_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_9} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_9} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_9} ${REG_CFG_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_9} ${REG_CFG_1} 0x00 + + #ZQSFP ABS0_15, ABS16_31, INT0_15, INT16_31, PSU1_PWROFF, PSU2_PWROFF + #command byte 2/3, output logic level is 0 + #command byte 4/5, polarity is not inverted + #command byte 6/7, I/O 1.0 and I/O 1.1 are output, others are input + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_10} ${REG_OUT_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_10} ${REG_OUT_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_10} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_10} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_10} ${REG_CFG_0} 0xFF + i2cset -y -r ${NUM_MUX_9548_1_CHAN3} ${I2C_ADDR_MUX_9535_10} ${REG_CFG_1} 0xFC +} + +#GPIO Init Utility Function +function _gpio_export { + local gpio_n=$1 + local direction=$2 + local active_low=$3 + local value=$4 + + if [ -z "${gpio_n}" ]; then + echo "[_gpio_init] gpio_n(${gpio_n}) is not provided" + return + fi + if [ "${gpio_n}" -lt "0" ] || [ "${gpio_n}" -gt "255" ]; then + echo "[_gpio_init] gpio_n(${gpio_n}) is invalid value" + return + fi + + #export gpio + echo ${gpio_n} > ${PATH_SYS_GPIO}/export + + #set gpio direction + echo ${direction} > ${PATH_SYS_GPIO}/gpio${gpio_n}/direction + + #set gpio active_low + echo ${active_low} > ${PATH_SYS_GPIO}/gpio${gpio_n}/active_low + + #set value + if [ ! -z "${value}" ]; then + echo ${value} > ${PATH_SYS_GPIO}/gpio${gpio_n}/value + fi +} + +#set gpiomap +function _set_gpiomap { + local gpio_n=$1 + local alias=$2 + + #create gpiomap dir if not exist + mkdir -p $PATH_GPIOMAP + + #check gpio_n exists in sysfs + if [ ! -L ${PATH_SYS_GPIO}/gpio${gpio_n} ]; then + echo "${PATH_SYS_GPIO}/gpio${gpio_n} does not exist." + return + fi + + #create or update link + ln -sf ${PATH_SYS_GPIO}/gpio${gpio_n} ${PATH_GPIOMAP}/${alias} +} + +#clear gpiomap +function _clear_gpiomap { + #delete gpiomap dir + rm -rf ${PATH_GPIOMAP} +} + +#GPIO Init +function _i2c_gpio_init { + local i=0 + + #ABS Port 0-31 + echo "pca9535 ${I2C_ADDR_MUX_9535_1}" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN1}/new_device + echo "pca9535 ${I2C_ADDR_MUX_9535_0}" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN1}/new_device + for i in {224..255} + do + _gpio_export ${i} ${DIR_IN} ${ACTIVE_LOW} + _set_gpiomap ${i} "QSFP$(( i - 223 ))_ABS" + done + + #INT Port 0-31 + echo "pca9535 ${I2C_ADDR_MUX_9535_3}" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN1}/new_device + echo "pca9535 ${I2C_ADDR_MUX_9535_2}" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN1}/new_device + for i in {192..223} + do + _gpio_export ${i} ${DIR_IN} ${ACTIVE_LOW} + _set_gpiomap ${i} "QSFP$(( i - 191 ))_INT" + done + + #LP Mode Port 0-31 + echo "pca9535 ${I2C_ADDR_MUX_9535_5}" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN2}/new_device + echo "pca9535 ${I2C_ADDR_MUX_9535_4}" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN2}/new_device + for i in {160..191} + do + _gpio_export ${i} ${DIR_OUT} ${ACTIVE_HIGH} + _set_gpiomap ${i} "QSFP$(( i - 159 ))_LPMODE" + done + + #RST Port 0-31 + echo "pca9535 ${I2C_ADDR_MUX_9535_7}" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN2}/new_device + echo "pca9535 ${I2C_ADDR_MUX_9535_6}" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN2}/new_device + for i in {128..159} + do + _gpio_export ${i} ${DIR_OUT} ${ACTIVE_LOW} + _set_gpiomap ${i} "QSFP$(( i - 127 ))_RST" + #Reset QSFP + echo "${ACTIVE_LOW}" > ${PATH_SYS_GPIO}/gpio${i}/value + echo "${ACTIVE_HIGH}" > ${PATH_SYS_GPIO}/gpio${i}/value + done + + #MODSEL Port 0-31 + echo "pca9535 ${I2C_ADDR_MUX_9535_9}" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN3}/new_device + echo "pca9535 ${I2C_ADDR_MUX_9535_8}" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN3}/new_device + for i in {96..127} + do + _gpio_export ${i} ${DIR_OUT} ${ACTIVE_LOW} + _set_gpiomap ${i} "QSFP$(( i - 95 ))_MODSEL" + done +} + +#GPIO DeInit +function _i2c_gpio_deinit { + echo ${I2C_ADDR_MUX_9535_0} > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN1}/delete_device + echo ${I2C_ADDR_MUX_9535_1} > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN1}/delete_device + echo ${I2C_ADDR_MUX_9535_2} > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN1}/delete_device + echo ${I2C_ADDR_MUX_9535_3} > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN1}/delete_device + echo ${I2C_ADDR_MUX_9535_4} > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN2}/delete_device + echo ${I2C_ADDR_MUX_9535_5} > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CHAN2}/delete_device + _clear_gpiomap +} + +#Set FAN Tray LED +function _i2c_led_fan_tray_status_set { + echo "FAN Tray Status Setup" + + #check W83795 exists in hwmon2 + if [ ! -e "${PATH_HWMON_W83795_DEVICE}" ]; then + echo "FAIL, W83795 not found in path ${PATH_HWMON_W83795_DEVICE}" + return + fi + #FAN Status get + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN2_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan2_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN4_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan4_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN6_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan6_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + FAN8_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan8_alarm` + + FAN_TRAY=1 + if [ "${FAN1_ALARM}" == "0" ] && [ "${FAN2_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + + FAN_TRAY=2 + if [ "${FAN3_ALARM}" == "0" ] && [ "${FAN4_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + + FAN_TRAY=3 + if [ "${FAN5_ALARM}" == "0" ] && [ "${FAN6_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + + FAN_TRAY=4 + if [ "${FAN7_ALARM}" == "0" ] && [ "${FAN8_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi +} + +#Test FAN Tray LED +function _i2c_led_fan_tray_test { + echo "FAN Tray LED Test" + + for i in {1..4} + do + FAN_TRAY=$i + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + _pause 'Check Fan Tray ${FAN_TRAY} LED green light and Press [Enter] key to continue...' + + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + _pause 'Check Fan Tray ${FAN_TRAY} LED amber light and Press [Enter] key to continue...' + done +} +#Set FAN LED +function _i2c_led_fan_status_set { + echo "FAN Status Setup" + + #check W83795 exists in hwmon2 + if [ ! -e "${PATH_HWMON_W83795_DEVICE}" ]; then + echo "FAIL, W83795 not found in path ${PATH_HWMON_W83795_DEVICE}" + return + fi + + #PSU Status set + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN2_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan2_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN4_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan4_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN6_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan6_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + FAN8_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan8_alarm` + + if [ "${FAN1_ALARM}" == "0" ] && [ "${FAN2_ALARM}" == "0" ] \ + && [ "${FAN3_ALARM}" == "0" ] && [ "${FAN4_ALARM}" == "0" ] \ + && [ "${FAN5_ALARM}" == "0" ] && [ "${FAN6_ALARM}" == "0" ] \ + && [ "${FAN7_ALARM}" == "0" ] && [ "${FAN8_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + fi +} + +#Set Power Supply LED +function _i2c_led_psu_status_set { + echo "PSU LED Status Setup" + + #PSU Status set + _i2c_psu_status + + #PSU1 Status + if [ "${psu1Exist}" == ${PSU_EXIST} ]; then + if [ "${psu1PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + fi + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + fi + + #PSU2 Status + if [ "${psu2Exist}" == ${PSU_EXIST} ]; then + if [ "${psu2PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + fi + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + fi +} + +#LED Test +function _i2c_led_test { + echo "=========================================================" + echo "# Description: I2C LED TEST..." + echo "=========================================================" + #sys led (green) + _i2c_reset_led + COLOR_LED="green" + ONOFF_LED="on" + _i2c_sys_led + _pause 'Check SYS LED green light and Press [Enter] key to continue...' + #sys led (amber) + _i2c_reset_led + COLOR_LED="amber" + ONOFF_LED="on" + _i2c_sys_led + _pause 'Check SYS LED amber light and Press [Enter] key to continue...' + + #FAN led (green) + _i2c_reset_led + COLOR_LED="green" + ONOFF_LED="on" + _i2c_fan_led + _pause 'Check FAN LED green light and Press [Enter] key to continue...' + #FAN led (amber) + _i2c_reset_led + COLOR_LED="amber" + ONOFF_LED="on" + _i2c_fan_led + _pause 'Check FAN LED amber light and Press [Enter] key to continue...' + + #PSU2 led (green) + _i2c_reset_led + COLOR_LED="green" + ONOFF_LED="on" + _i2c_psu2_led + _pause 'Check PSU2 LED green light and Press [Enter] key to continue...' + #PSU2 led (amber) + _i2c_reset_led + COLOR_LED="amber" + ONOFF_LED="on" + _i2c_psu2_led + _pause 'Check PSU2 LED amber light and Press [Enter] key to continue...' + + #PSU1 led (green) + _i2c_reset_led + COLOR_LED="green" + ONOFF_LED="on" + _i2c_psu1_led + _pause 'Check PSU1 LED green light and Press [Enter] key to continue...' + #PSU1 led (amber) + _i2c_reset_led + COLOR_LED="amber" + ONOFF_LED="on" + _i2c_psu1_led + _pause 'Check PSU1 LED amber light and Press [Enter] key to continue...' + + #Turn OFF All LED + _i2c_reset_led + _pause 'Check turn off all LEDs and Press [Enter] key to continue...' + echo "done..." + + #sys led (green) + COLOR_LED="green" + ONOFF_LED="on" + _i2c_sys_led +} + +#Set QSFP Port variable +function _qsfp_port_i2c_var_set { + local port=$1 + case ${port} in + 1|2|3|4|5|6|7|8) + i2cbus=${NUM_MUX_9548_1_CHAN1} + regAddr=0x20 + dataAddr=0 + eeprombusbase=${NUM_MUX_9548_2_CHAN0} + gpioBase=224 + ;; + 9|10|11|12|13|14|15|16) + i2cbus=${NUM_MUX_9548_1_CHAN1} + regAddr=0x20 + dataAddr=1 + eeprombusbase=${NUM_MUX_9548_3_CHAN0} + gpioBase=224 + ;; + 17|18|19|20|21|22|23|24) + i2cbus=${NUM_MUX_9548_1_CHAN1} + regAddr=0x21 + dataAddr=0 + eeprombusbase=${NUM_MUX_9548_4_CHAN0} + gpioBase=240 + ;; + 25|26|27|28|29|30|31|32) + i2cbus=${NUM_MUX_9548_1_CHAN1} + regAddr=0x21 + dataAddr=1 + eeprombusbase=${NUM_MUX_9548_5_CHAN0} + gpioBase=240 + ;; + *) + echo "Please input 1~32" + ;; + esac +} + +#Set QSFP Port variable +function _qsfp_eeprom_var_set { + local port=$1 + eeprombusidx=$(( ${port} % 8)) + case $eeprombusidx in + 1) + eeprombus=$(( $eeprombusbase + 0 )) + ;; + 2) + eeprombus=$(( $eeprombusbase + 1 )) + ;; + 3) + eeprombus=$(( $eeprombusbase + 2 )) + ;; + 4) + eeprombus=$(( $eeprombusbase + 3 )) + ;; + 5) + eeprombus=$(( $eeprombusbase + 4 )) + ;; + 6) + eeprombus=$(( $eeprombusbase + 5 )) + ;; + 7) + eeprombus=$(( $eeprombusbase + 6 )) + ;; + 0) + eeprombus=$(( $eeprombusbase + 7 )) + ;; + esac + eepromAddr=${I2C_ADDR_QSFP_EEPROM} +} + +#Get QSFP EEPROM Information +function _i2c_qsfp_eeprom_get { + + _qsfp_port_i2c_var_set ${QSFP_PORT} + + #status: 0 -> Down, 1 -> Up + status=`cat ${PATH_SYS_GPIO}/gpio$(( gpioBase + (QSFP_PORT-1)%16 ))/value` + echo $status + + if [ $status = 0 ]; then + exit + fi + + _qsfp_eeprom_var_set ${QSFP_PORT} + + cat ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/eeprom | hexdump -C +} + +#Init QSFP EEPROM +function _i2c_qsfp_eeprom_init { + echo -n "QSFP EEPROM INIT..." + + #Action check + action=$1 + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init 1-32 ports EEPROM + local i + for i in {1..32}; + do + _qsfp_port_i2c_var_set ${i} + + _qsfp_eeprom_var_set ${i} + + if [ "${action}" == "new" ] && \ + ! [ -L ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr) ]; then + echo "sff8436 $eepromAddr" > ${PATH_SYS_I2C_DEVICES}/i2c-$eeprombus/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr) ]; then + echo "$eepromAddr" > ${PATH_SYS_I2C_DEVICES}/i2c-$eeprombus/delete_device + fi + done + echo "DONE" +} + +#Init Main Board EEPROM +function _i2c_mb_eeprom_init { + echo -n "Main Board EEPROM INIT..." + + #Action check + action=$1 + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init MB EEPROM + if [ "${action}" == "new" ] && \ + ! [ -L ${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MB_EEPROM}-$(printf "%04x" $I2C_ADDR_MB_EEPROM) ]; then + echo "mb_eeprom ${I2C_ADDR_MB_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_MB_EEPROM}/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MB_EEPROM}-$(printf "%04x" $I2C_ADDR_MB_EEPROM) ]; then + echo "${I2C_ADDR_MB_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_MB_EEPROM}/delete_device + fi + echo "DONE" +} + +#get QSFP Status +function _i2c_qsfp_status_get { + + _qsfp_port_i2c_var_set ${QSFP_PORT} + + #status: 0 -> Down, 1 -> Up + + status=`cat ${PATH_SYS_GPIO}/gpio$(( gpioBase + (QSFP_PORT-1)%16 ))/value` + echo "status=$status" +} + +#get QSFP Type +function _i2c_qsfp_type_get { + + _qsfp_port_i2c_var_set ${QSFP_PORT} + + _qsfp_eeprom_var_set ${QSFP_PORT} + + #Get QSFP EEPROM info + qsfp_info=$(base64 ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/eeprom) + + identifier=$(echo $qsfp_info | base64 -d -i | hexdump -s 128 -n 1 -e '"%x"') + connector=$(echo $qsfp_info | base64 -d -i | hexdump -s 130 -n 1 -e '"%x"') + transceiver=$(echo $qsfp_info | base64 -d -i | hexdump -s 131 -n 1 -e '"%x"') + + echo "identifier=$identifier" + echo "connector=$connector" + echo "transceiver=$transceiver" +} + +#Init PSU Kernel Module +function _i2c_psu_init { + echo "=========================================================" + echo "# Description: I2C PSU Init" + echo "=========================================================" + modprobe ingrasys_s8810_32q_psu + + echo "psu1 ${I2C_ADDR_PSU_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_PSU1_EEPROM}/new_device + echo "psu2 ${I2C_ADDR_PSU_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_PSU2_EEPROM}/new_device +} + +#Get PSU EEPROM Information +function _i2c_psu_eeprom_get { + local eeprom_psu1="" + local eeprom_psu2="" + + echo "=========================================================" + echo "# Description: I2C PSU EEPROM Get..." + echo "=========================================================" + + eeprom_psu1="${PATH_SYSFS_PSU1}/psu_eeprom" + cat ${eeprom_psu1} | hexdump -C + + eeprom_psu2="${PATH_SYSFS_PSU2}/psu_eeprom" + cat ${eeprom_psu2} | hexdump -C +} + +#Get MotherBoard EEPROM Information +function _i2c_mb_eeprom_get { + echo "=========================================================" + echo "# Description: I2C MB EEPROM Get..." + echo "=========================================================" + + ## MB EEPROM + cat ${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MB_EEPROM}-$(printf "%04x" $I2C_ADDR_MB_EEPROM)/eeprom | hexdump -C + echo "done..." +} + +#Set FAN Tray LED +function _i2c_fan_tray_led { + local value=0 + case ${FAN_TRAY} in + 1) + ioPort=$REG_OUT_0 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x01 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x02 + fi + ;; + 2) + ioPort=$REG_OUT_0 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + 3) + ioPort=$REG_OUT_1 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x01 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x02 + fi + ;; + 4) + ioPort=$REG_OUT_1 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + *) + echo "Please input 1~4" + exit + ;; + esac + + if [ "${ONOFF_LED}" == "on" ]; then + value=0xFF + elif [ "${ONOFF_LED}" == "off" ]; then + value=0x00 + else + echo "Invalid Parameters ${ONOFF_LED}, Exit!!!" + _help + exit ${FALSE} + fi + i2cset -m $mask -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_FAN} $ioPort $value + + #echo "done..." +} + +#Set System Status LED +function _i2c_sys_led { + local green_mask=${BIT_MASK[7]} + local amber_mask=${BIT_MASK[6]} + + _i2c_set_led $green_mask $amber_mask +} + +#Set FAN LED +function _i2c_fan_led { + local green_mask=${BIT_MASK[3]} + local amber_mask=${BIT_MASK[2]} + + _i2c_set_led $green_mask $amber_mask +} + +#Set PSU1 LED +function _i2c_psu1_led { + local green_mask=${BIT_MASK[1]} + local amber_mask=${BIT_MASK[0]} + + _i2c_set_led $green_mask $amber_mask +} + +#Set PSU2 LED +function _i2c_psu2_led { + local green_mask=${BIT_MASK[5]} + local amber_mask=${BIT_MASK[4]} + + _i2c_set_led $green_mask $amber_mask +} + +#Set LEDs in LED Board +function _i2c_set_led { + local green_mask=$1 + local amber_mask=$2 + local mask=0 + local value=0 + + + if [ "${COLOR_LED}" == "green" ]; then + mask=$green_mask + elif [ "${COLOR_LED}" == "amber" ]; then + mask=$amber_mask + else + echo "Invalid Parameters ${COLOR_LED}, Exit!!!" + _help + exit ${FALSE} + fi + + # Singals are active_low + if [ "${ONOFF_LED}" == "on" ]; then + value=0x00 + elif [ "${ONOFF_LED}" == "off" ]; then + value=0xFF + else + echo "Invalid Parameters ${ONOFF_LED}, Exit!!!" + _help + exit ${FALSE} + fi + + i2cset -m $mask -y -r ${I2C_BUS_LED_BOARD} ${I2C_ADDR_MUX_9535_LED} ${REG_OUT_0} $value +} + +#Reset all system leds +function _i2c_reset_led { + i2cset -y ${I2C_BUS_LED_BOARD} ${I2C_ADDR_MUX_9535_LED} ${REG_OUT_0} 0xFF +} + +#Get Board Version and Type +function _i2c_board_type_get { + local reg_board=0x00 + local reg_ext_board=0x07 + local boardType=0 + local boardBuildRev=0 + local boardHwRev=0 + local boardId=0 + local extBoardType=0 + + boardType=`i2cget -y ${NUM_I801_DEVICE} ${I2C_ADDR_CPLD} ${reg_board}` + #Bit 0-1 + boardBuildRev=$((($boardType) & 0x03)) + #Bit 2-3 + boardHwRev=$((($boardType) >> 2 & 0x03)) + #Bit 4-7 + boardId=$((($boardType) >> 4)) + + extBoardType=`i2cget -y ${NUM_I801_DEVICE} ${I2C_ADDR_CPLD} ${reg_ext_board}` + #Bit 0-3 + extBoardType=$((($extBoardType) & 0x0F)) + + printf "BOARD_ID=0x%02x, HW Rev=%d, Build Rev=%d, Ext_BOARD_ID=0x%02x\n" $boardId $boardHwRev $boardBuildRev $extBoardType + +} + +#Get CPLD Version +function _i2c_cpld_version { + local reg_cpld_rev=0x01 + cpldRev=`i2cget -y ${NUM_I801_DEVICE} ${I2C_ADDR_CPLD} ${reg_cpld_rev}` + cpldRelease=$((($cpldRev) >> 6 & 0x01)) + cpldVersion=$((($cpldRev) & 0x3F)) + printf "CPLD is %s version(0:RD 1:Release), Revision is 0x%02x\n" $cpldRelease $cpldVersion + +} + +#Get PSU Status +function _i2c_psu_status { + local psu_abs="" + + psu1PwGood=`cat ${PATH_SYSFS_PSU1}/psu_pg` + psu_abs=`cat ${PATH_SYSFS_PSU1}/psu_abs` + if [ "$psu_abs" == "0" ]; then + psu1Exist=1 + else + psu1Exist=0 + fi + + psu2PwGood=`cat ${PATH_SYSFS_PSU2}/psu_pg` + psu_abs=`cat ${PATH_SYSFS_PSU2}/psu_abs` + if [ "$psu_abs" == "0" ]; then + psu2Exist=1 + else + psu2Exist=0 + fi + + printf "PSU1 Exist:%x PSU1 PW Good:%d\n" $psu1Exist $psu1PwGood + printf "PSU2 Exist:%d PSU2 PW Good:%d\n" $psu2Exist $psu2PwGood +} + +#Increase read socket buffer for CoPP Test +function _config_rmem { + echo "109430400" > /proc/sys/net/core/rmem_max +} + +#Main Function +function _main { + start_time_str=`date` + start_time_sec=$(date +%s) + + if [ "${EXEC_FUNC}" == "help" ]; then + _help + elif [ "${EXEC_FUNC}" == "i2c_init" ]; then + _i2c_init + elif [ "${EXEC_FUNC}" == "i2c_deinit" ]; then + _i2c_deinit + elif [ "${EXEC_FUNC}" == "i2c_temp_init" ]; then + _i2c_temp_init + elif [ "${EXEC_FUNC}" == "i2c_fan_init" ]; then + _i2c_fan_init + elif [ "${EXEC_FUNC}" == "i2c_volmon_init" ]; then + _i2c_volmon_init + elif [ "${EXEC_FUNC}" == "i2c_io_exp_init" ]; then + _i2c_io_exp_init + elif [ "${EXEC_FUNC}" == "i2c_gpio_init" ]; then + _i2c_gpio_init + elif [ "${EXEC_FUNC}" == "i2c_gpio_deinit" ]; then + _i2c_gpio_deinit + elif [ "${EXEC_FUNC}" == "i2c_led_test" ]; then + _i2c_led_test + elif [ "${EXEC_FUNC}" == "i2c_mb_eeprom_get" ]; then + _i2c_mb_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_psu_eeprom_get" ]; then + _i2c_psu_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_eeprom_get" ]; then + _i2c_qsfp_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_eeprom_init" ]; then + _i2c_qsfp_eeprom_init ${QSFP_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_mb_eeprom_init" ]; then + _i2c_mb_eeprom_init ${MB_EEPROM_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_qsfp_status_get" ]; then + _i2c_qsfp_status_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_type_get" ]; then + _i2c_qsfp_type_get + elif [ "${EXEC_FUNC}" == "i2c_led_psu_status_set" ]; then + _i2c_led_psu_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_status_set" ]; then + _i2c_led_fan_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_tray_status_set" ]; then + _i2c_led_fan_tray_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_tray_test" ]; then + _i2c_led_fan_tray_test + elif [ "${EXEC_FUNC}" == "i2c_sys_led" ]; then + _i2c_sys_led + elif [ "${EXEC_FUNC}" == "i2c_fan_led" ]; then + _i2c_fan_led + elif [ "${EXEC_FUNC}" == "i2c_fan_tray_led" ]; then + _i2c_fan_tray_led + elif [ "${EXEC_FUNC}" == "i2c_psu1_led" ]; then + _i2c_psu1_led + elif [ "${EXEC_FUNC}" == "i2c_psu2_led" ]; then + _i2c_psu2_led + elif [ "${EXEC_FUNC}" == "i2c_board_type_get" ]; then + _i2c_board_type_get + elif [ "${EXEC_FUNC}" == "i2c_cpld_version" ]; then + _i2c_cpld_version + elif [ "${EXEC_FUNC}" == "i2c_psu_status" ]; then + _i2c_psu_status + elif [ "${EXEC_FUNC}" == "i2c_test_all" ]; then + _i2c_deinit + _i2c_init + _i2c_led_test + _i2c_psu_eeprom_get + _i2c_mb_eeprom_get + _i2c_board_type_get + _i2c_cpld_version + _i2c_psu_status + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + end_time_str=`date` + end_time_sec=$(date +%s) + diff_time=$[ ${end_time_sec} - ${start_time_sec} ] + echo "Start Time: ${start_time_str} (${start_time_sec})" + echo "End Time : ${end_time_str} (${end_time_sec})" + echo "Total Execution Time: ${diff_time} sec" + + echo "done!!!" +} + +_main diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/utils/qsfp_monitor.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/utils/qsfp_monitor.sh new file mode 100755 index 000000000000..249f179216a6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/utils/qsfp_monitor.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# Copyright (C) 2017 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=3 +I2C_UTILS="/usr/sbin/i2c_utils.sh" +QSFP_SI_SCRIPT="/usr/sbin/qsfp_si_cfg.sh" +QSFP_ARRAY=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + +#QSFP SI monitor +function _qsfp_si_monitor { + local i + local status + for i in {0..31}; + do + status=`${I2C_UTILS} i2c_qsfp_status_get $(expr $i + 1) | egrep '^status=.*$' | sed -e 's/status=//g'` + if [ "${status}" == "1" ]; then + _qsfp_type_check $i + fi + done +} + +#QSFP type +function _qsfp_type_check { + local port=$1 + local qsfp_type=`${I2C_UTILS} i2c_qsfp_type_get $(expr $port + 1)` + local identifier=`echo "$qsfp_type" | grep '^identifier=.*$' | sed -e 's/identifier=//g'` + if [ "${identifier}" == "11" ]; then + connector=`echo "$qsfp_type" | grep '^connector=.*$' | sed -e 's/connector=//g'` + case ${connector} in + 21|23) + #DAC + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to DAC" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} dac $port >/dev/null + fi + ;; + *) + #Optical + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to Optical" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} optical $port >/dev/null + fi + ;; + esac + fi +} + +#Docker exist check +function _docker_swss_check { + while true + do + # Check if syncd starts + result=`docker exec -i swss bash -c "echo -en \"SELECT 1\\nHLEN HIDDEN\" | redis-cli | sed -n 2p"` #TBD FIX ME + if [ "$result" == "3" ]; then + return + fi + sleep $INTERVAL + done +} + +#Docker exist check +function _qsfp_si_cfg_script_check { + + if [ -f ${QSFP_SI_SCRIPT} ] && [ -x ${QSFP_SI_SCRIPT} ]; then + echo "SI Script exists. Start monitor." + return + else + echo "SI Script not exist. Exit monitor." + exit + fi +} + +# main function +function _main { + #Check SI Script + _qsfp_si_cfg_script_check + #Check docker swss is running + _docker_swss_check + while true + do + _qsfp_si_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/utils/s8810_32q_monitor.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/utils/s8810_32q_monitor.sh new file mode 100755 index 000000000000..974da6d5001f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8810-32q/utils/s8810_32q_monitor.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=5 +I2C_UTILS="/usr/sbin/i2c_utils.sh" + + +# LED status monitor +function _led_monitor { + ${I2C_UTILS} i2c_led_fan_status_set >/dev/null + ${I2C_UTILS} i2c_led_psu_status_set >/dev/null + ${I2C_UTILS} i2c_led_fan_tray_status_set >/dev/null +} + +# main function +function _main { + while true + do + _led_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/README.md b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/README.md new file mode 100644 index 000000000000..2042625f631c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/README.md @@ -0,0 +1,185 @@ +# Ingrasys S8900-54XC Platform Driver for SONiC + +Copyright (C) 2016 Ingrasys, Inc. + +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 3 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, see . + + +## Licensing terms + +The Licensing terms of the files within this project is split 2 parts. +The Linux kernel is released under the GNU General Public License version 2. +All other code is released under the GNU General Public License version 3. +Please see the LICENSE file for copies of both licenses. + +## Contents of this package + + - service/ + > Service config files for platform initialization and monitoring + - utils/ + > Scripts useful during platform bringup and sysfs function + - conf/ + > Platform configure files. + +## Kernel modules and drivers + +The driver for interacting with Ingrasys S8900-54XC is contained in the I2C +kernel module and initialization script. The initialization script loads +the modules in the correct order. It has been built and tested against +the Linux 3.16 kernel. + +The initialization script will modprobe the needed modules, navigate to the +module's device directory in sysfs, and write configuration data to +the kernel module. + +### IGB + +This is OOB Ports on front panel for management plane. + +The IGB module must be loaded first on Ingrasys S8900-54XC platform. + +### I2C i801 + +The I2C i801 on Ingrasys S8900-54XC can be found in +`/sys/bus/i2c/devices/i2c-0/` + +This is I2C bus for Clock Gen, DIMM channel and digital potentiometers. + +The i801 module must be loaded second on Ingrasys S8900-54XC. + +### I2C iSMT + +The I2C iSMT module on S8900-54XC can be found in +`/sys/bus/i2c/devices/i2c-1/` + +This is I2C bus for CPLD, HWM, power controller and I2C Switches. + +The i801 module must be loaded third on Ingrasys S8900-54XC. + +### I2C PCA9548 +The PCA9548 module on S8900-54XC can be found in +`/sys/bus/i2c/devices/i2c-2/` , `/sys/bus/i2c/devices/i2c-3/`, +`/sys/bus/i2c/devices/i2c-4/`, `/sys/bus/i2c/devices/i2c-5/`, +`/sys/bus/i2c/devices/i2c-6/`, `/sys/bus/i2c/devices/i2c-7/`, +`/sys/bus/i2c/devices/i2c-8/`, `/sys/bus/i2c/devices/i2c-9/`. + +The pca9548 module for zQSFP module get/set functions, PSU information, +fan status and EEPROM. + +## Hardware components + +The hardware components are initialized in the init script on S8900-54XC. +The following describes manual initialization as well as interaction. +The examples below are just for Ingrasys S8900-54XC platform. + +### Hardware initialization + +When the sonic-platform-ingrasys-s8900-54xc package is installed on the S8900-54XC, +it is automatically initialized. If you want to manual initialization, the +utility command usage as follows: +``` + i2c_utils.sh i2c_init +``` + +### EEPROM + +The EEPROM is including the board SKU, model name, vendor name, serial number, +and other information can be accessed with the specific eeprom kernel module. +After using `modprobe eeprom_mb` to detect the eeprom, it will show up in sysfs. + +The hexdump utility can be used to decode the raw output of the EEPROM. +For example, +``` + bash# echo "mb_eeprom 0x54" > /sys/bus/i2c/devices/i2c-9/new_device + bash# cat /sys/bus/i2c/drivers/mb_eeprom/9-0054/eeprom | hexdump -C +``` + +### Front panel LEDs + +LEDs can be setup on/off by using i2c utility `/usr/sbin/i2c_utils.sh`. +Utility function command usage as follows: + +``` +Status LED: + i2c_utils.sh i2c_sys_led green|amber on|off + +Fan status LED: + i2c_utils.sh i2c_fan_led green|amber on|off + +PSU1 status LED: + i2c_utils.sh i2c_psu1_led green|amber on|off + +PSU2 status LED: + i2c_utils.sh i2c_psu2_led green|amber on|off + +``` +QSFP Module Port LEDs control by ASIC library. + + +### Fan speed + +Fan speed are controlled by the w83795 kernel module. +It can be found in `/sys/class/hwmon/hwmon1/device/`. +If they were compiled as modules, you will need to modprobe w83795 for +their sysfs entries to show up. Each fan has an `fan_input` file +for reading the fan speed. And `pwm1` setting fan1 to fan4, +`pwm2` setting fan5 to fan8. + +There is docker-platform-monitor container installed fancontrol package that can +automatic control platform fan speed. + + +### Temperature sensors + +Temperature sensors are controlled by the w83795 kernel +module. It can be found in `/sys/class/hwmon/hwmon1/device/`. +If they were compiled as modules, then you will need to modprobe w83795 for +their sysfs entries to show up. +`temp1_input` is front MAC temperature sensor. `temp2_input` is rear MAC +temperature sensor. + +There is docker-platform-monitor container installed lm-sensors package that can +monitor platform temperature. And `sensors` command can show each +temperature sensors status. + +### Power supplies + +Power supplies status and its EEPROM info can be used i2c utility +`/usr/sbin/i2c_utils.sh` to get. +The usage as follows: +``` +PSU EEPROM: + i2c_utils.sh i2c_psu_eeprom_get + hexdump -C psu0.rom + hexdump -C psu1.rom + +PSU Status: + i2c_utils.sh i2c_psu_status +``` + +### QSFPs +QSFP modules are managed by the pca9548 kernel driver. +The i2c utility `/usr/sbin/i2c_utils.sh` can be used to get status and +module EEPROM informations. +The usage as follows: +``` +QSFP EEPROM: + i2c_utils.sh i2c_qsfp_eeprom_get [1-32] + +QSFP Insert Event: + i2c_utils.sh i2c_qsfp_eeprom_get [1-32] + 0 => not insert + 1 => inserted +``` + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/modules/Makefile b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/modules/Makefile new file mode 100644 index 000000000000..a0042543dff5 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/modules/Makefile @@ -0,0 +1,2 @@ +obj-m := eeprom_mb.o +obj-m+= i2c_cpld.o diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/modules/eeprom_mb.c b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/modules/eeprom_mb.c new file mode 100644 index 000000000000..0886cad58eb3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/modules/eeprom_mb.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 1998, 1999 Frodo Looijaard and + * Philip Edelbrock + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003 IBM Corp. + * Copyright (C) 2004 Jean Delvare + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, I2C_CLIENT_END }; + + +/* Size of EEPROM in bytes */ +#define EEPROM_SIZE 256 + +#define SLICE_BITS (6) +#define SLICE_SIZE (1 << SLICE_BITS) +#define SLICE_NUM (EEPROM_SIZE/SLICE_SIZE) + +/* Each client has this additional data */ +struct eeprom_data { + struct mutex update_lock; + u8 valid; /* bitfield, bit!=0 if slice is valid */ + unsigned long last_updated[SLICE_NUM]; /* In jiffies, 8 slices */ + u8 data[EEPROM_SIZE]; /* Register values */ +}; + + +static void mb_eeprom_update_client(struct i2c_client *client, u8 slice) +{ + struct eeprom_data *data = i2c_get_clientdata(client); + int i, j; + int ret; + int addr; + + mutex_lock(&data->update_lock); + + if (!(data->valid & (1 << slice)) || + time_after(jiffies, data->last_updated[slice] + 300 * HZ)) { + dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice); + + addr = slice << SLICE_BITS; + + ret = i2c_smbus_write_byte_data(client, ((u8)addr >> 8) & 0xFF, (u8)addr & 0xFF); + /* select the eeprom address */ + if (ret < 0) { + dev_err(&client->dev, "address set failed\n"); + goto exit; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE)) { + goto exit; + } + + for (i = slice << SLICE_BITS; i < (slice + 1) << SLICE_BITS; i+= SLICE_SIZE) { + for (j = i; j < (i+SLICE_SIZE); j++) { + int res; + + res = i2c_smbus_read_byte(client); + if (res < 0) { + goto exit; + } + + data->data[j] = res & 0xFF; + } + } + + data->last_updated[slice] = jiffies; + data->valid |= (1 << slice); + } +exit: + mutex_unlock(&data->update_lock); +} + +static ssize_t mb_eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct eeprom_data *data = i2c_get_clientdata(client); + u8 slice; + + if (off > EEPROM_SIZE) { + return 0; + } + if (off + count > EEPROM_SIZE) { + count = EEPROM_SIZE - off; + } + if (count == 0) { + return 0; + } + + /* Only refresh slices which contain requested bytes */ + for (slice = off >> SLICE_BITS; slice <= (off + count - 1) >> SLICE_BITS; slice++) { + mb_eeprom_update_client(client, slice); + } + + memcpy(buf, &data->data[off], count); + + return count; +} + +static struct bin_attribute mb_eeprom_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO, + }, + .size = EEPROM_SIZE, + .read = mb_eeprom_read, +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int mb_eeprom_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all + addresses 0x50-0x57, but we only care about 0x50. So decline + attaching to addresses >= 0x51 on DDC buses */ + if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x51) { + return -ENODEV; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE) + && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + return -ENODEV; + } + + strlcpy(info->type, "eeprom", I2C_NAME_SIZE); + + return 0; +} + +static int mb_eeprom_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct eeprom_data *data; + int err; + + if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + memset(data->data, 0xff, EEPROM_SIZE); + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &mb_eeprom_attr); + if (err) { + goto exit_kfree; + } + + return 0; + +exit_kfree: + kfree(data); +exit: + return err; +} + +static int mb_eeprom_remove(struct i2c_client *client) +{ + sysfs_remove_bin_file(&client->dev.kobj, &mb_eeprom_attr); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id mb_eeprom_id[] = { + { "mb_eeprom", 0 }, + { } +}; + +static struct i2c_driver mb_eeprom_driver = { + .driver = { + .name = "mb_eeprom", + }, + .probe = mb_eeprom_probe, + .remove = mb_eeprom_remove, + .id_table = mb_eeprom_id, + + .class = I2C_CLASS_DDC | I2C_CLASS_SPD, + .detect = mb_eeprom_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(mb_eeprom_driver); + +MODULE_AUTHOR("Wade "); +MODULE_DESCRIPTION("Ingrasys S8900 Mother Borad EEPROM driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/modules/i2c_cpld.c b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/modules/i2c_cpld.c new file mode 100644 index 000000000000..626c63177b50 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/modules/i2c_cpld.c @@ -0,0 +1,448 @@ +/* + * S9100-32X I2C CPLD driver + * + * Copyright (C) 2017 Ingrasys, Inc. + * Wade He + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "i2c_cpld.h" + +#ifdef DEBUG + #define DEBUG_PRINT(fmt, args...) \ + printk(KERN_INFO "%s[%d]: " fmt "\r\n", \ + __FUNCTION__, __LINE__, ##args) +#else + #define DEBUG_PRINT(fmt, args...) +#endif + +#define I2C_READ_BYTE_DATA(ret, lock, i2c_client, reg) \ +{ \ + mutex_lock(lock); \ + ret = i2c_smbus_read_byte_data(i2c_client, reg); \ + mutex_unlock(lock); \ +} +#define I2C_WRITE_BYTE_DATA(ret, lock, i2c_client, reg, val) \ +{ \ + mutex_lock(lock); \ + ret = i2c_smbus_write_byte_data(i2c_client, reg, val); \ + mutex_unlock(lock); \ +} + +/* CPLD sysfs attributes index */ +enum i2c_cpld_sysfs_attributes { + CPLD_ACCESS_REG, + CPLD_REGISTER_VAL, + CPLD_PORT_START, + CPLD_PORTS, + CPLD_VERSION, + CPLD_ID, + CPLD_BOARD_TYPE, + CPLD_EXT_BOARD_TYPE, + CPLD_PW_GOOD, + CPLD_PW_ABS, +}; + +/* CPLD sysfs attributes hook functions */ +static ssize_t read_access_register(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t write_access_register(struct device *dev, + struct device_attribute *da, const char *buf, size_t count); +static ssize_t read_register_value(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t write_register_value(struct device *dev, + struct device_attribute *da, const char *buf, size_t count); +static ssize_t read_cpld_version(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_board_type(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_ext_board_type(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_pw_good(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_pw_abs(struct device *dev, + struct device_attribute *da, char *buf); + +static LIST_HEAD(cpld_client_list); /* client list for cpld */ +static struct mutex list_lock; /* mutex for client list */ + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +struct cpld_data { + int index; /* CPLD index */ + struct mutex access_lock; /* mutex for cpld access */ + u8 access_reg; /* register to access */ +}; + +/* CPLD device id and data */ +static const struct i2c_device_id i2c_cpld_id[] = { + { "i2c_cpld", i2c_cpld }, + {} +}; + +/* Addresses scanned for i2c_cpld */ +static const unsigned short cpld_i2c_addr[] = { 0x33, I2C_CLIENT_END }; +/* platform sysfs object */ +extern struct kobject *s9230_64x_kobj; + + +/* define all support register access of cpld in attribute */ +static SENSOR_DEVICE_ATTR(cpld_access_register, S_IWUSR | S_IRUGO, + read_access_register, write_access_register, CPLD_ACCESS_REG); +static SENSOR_DEVICE_ATTR(cpld_register_value, S_IWUSR | S_IRUGO, + read_register_value, write_register_value, CPLD_REGISTER_VAL); +static SENSOR_DEVICE_ATTR(cpld_version, S_IRUGO, + read_cpld_version, NULL, CPLD_VERSION); +static SENSOR_DEVICE_ATTR(cpld_board_type, S_IRUGO, + read_board_type, NULL, CPLD_BOARD_TYPE); +static SENSOR_DEVICE_ATTR(cpld_ext_board_type, S_IRUGO, + read_ext_board_type, NULL, CPLD_EXT_BOARD_TYPE); +static SENSOR_DEVICE_ATTR(cpld_pw_good, S_IRUGO, + read_pw_good, NULL, CPLD_PW_GOOD); +static SENSOR_DEVICE_ATTR(cpld_pw_abs, S_IRUGO, + read_pw_abs, NULL, CPLD_PW_ABS); + + +/* define support attributes of cpldx , total 5 */ +/* cpld 1 */ +static struct attribute *i2c_cpld_attributes[] = { + &sensor_dev_attr_cpld_access_register.dev_attr.attr, + &sensor_dev_attr_cpld_register_value.dev_attr.attr, + &sensor_dev_attr_cpld_version.dev_attr.attr, + &sensor_dev_attr_cpld_board_type.dev_attr.attr, + &sensor_dev_attr_cpld_ext_board_type.dev_attr.attr, + &sensor_dev_attr_cpld_pw_good.dev_attr.attr, + &sensor_dev_attr_cpld_pw_abs.dev_attr.attr, + NULL +}; + +/* cpld 1 attributes group */ +static const struct attribute_group i2c_cpld_group = { + .attrs = i2c_cpld_attributes, +}; + +/* read access register from cpld data */ +static ssize_t read_access_register(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg = data->access_reg; + + return sprintf(buf, "0x%x\n", reg); +} + +/* write access register to cpld data */ +static ssize_t write_access_register(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + + if (kstrtou8(buf, 0, ®) < 0) + return -EINVAL; + + data->access_reg = reg; + return count; +} + +/* read the value of access register in cpld data */ +static ssize_t read_register_value(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg = data->access_reg; + int reg_val; + + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + + if (reg_val < 0) + return -1; + + return sprintf(buf, "0x%x\n", reg_val); +} + +/* wrtie the value to access register in cpld data */ +static ssize_t write_register_value(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + int ret = -EIO; + u8 reg = data->access_reg; + u8 reg_val; + + if (kstrtou8(buf, 0, ®_val) < 0) + return -EINVAL; + + I2C_WRITE_BYTE_DATA(ret, &data->access_lock, client, reg, reg_val); + + return count; +} + +/* get cpdl version register value */ +static ssize_t read_cpld_version(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_VERSION) { + reg = CPLD_VERSION_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get board type register value */ +static ssize_t read_board_type(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_BOARD_TYPE) { + reg = CPLD_BOARD_TYPE_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get extend board type register value */ +static ssize_t read_ext_board_type(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_EXT_BOARD_TYPE) { + reg = CPLD_EXT_BOARD_TYPE_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get extend board type register value */ +static ssize_t read_pw_good(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_PW_GOOD) { + reg = CPLD_PW_GOOD_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get extend board type register value */ +static ssize_t read_pw_abs(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_PW_ABS) { + reg = CPLD_PW_ABS_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* add valid cpld client to list */ +static void i2c_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = NULL; + + node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + if (!node) { + dev_info(&client->dev, + "Can't allocate cpld_client_node for index %d\n", + client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +/* remove exist cpld client in list */ +static void i2c_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + list_for_each(list_node, &cpld_client_list) { + cpld_node = list_entry(list_node, + struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + mutex_unlock(&list_lock); +} + +/* cpld drvier probe */ +static int i2c_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + struct cpld_data *data = NULL; + + data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* init cpld data for client */ + i2c_set_clientdata(client, data); + mutex_init(&data->access_lock); + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_info(&client->dev, + "i2c_check_functionality failed (0x%x)\n", + client->addr); + status = -EIO; + goto exit; + } + + + status = sysfs_create_group(&client->dev.kobj,&i2c_cpld_group); + + if (status) + goto exit; + + dev_info(&client->dev, "chip found\n"); + + /* add probe chip to client list */ + i2c_cpld_add_client(client); + + return 0; +exit: + sysfs_remove_group(&client->dev.kobj, &i2c_cpld_group); + return status; +} + +/* cpld drvier remove */ +static int i2c_cpld_remove(struct i2c_client *client) +{ + + sysfs_remove_group(&client->dev.kobj, &i2c_cpld_group); + + i2c_cpld_remove_client(client); + return 0; +} + +MODULE_DEVICE_TABLE(i2c, i2c_cpld_id); + +static struct i2c_driver i2c_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "i2c_cpld", + }, + .probe = i2c_cpld_probe, + .remove = i2c_cpld_remove, + .id_table = i2c_cpld_id, + .address_list = cpld_i2c_addr, +}; + +static int __init i2c_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&i2c_cpld_driver); +} + +static void __exit i2c_cpld_exit(void) +{ + i2c_del_driver(&i2c_cpld_driver); +} + +MODULE_AUTHOR("Wade He "); +MODULE_DESCRIPTION("Ingrasys S9100-32X Platform i2c cpld driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_cpld_init); +module_exit(i2c_cpld_exit); + + + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/modules/i2c_cpld.h b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/modules/i2c_cpld.h new file mode 100644 index 000000000000..e103b05c717a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/modules/i2c_cpld.h @@ -0,0 +1,197 @@ +/* + * + * S9100-32X I2C CPLD driver header file + * + * Copyright (C) 2017 Ingrasys, Inc. + * Wade He + * + * 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, see . + */ + +#ifndef I2C_CPLD_H +#define I2C_CPLD_H + +// remove debug before release +#define DEBUG + +/* CPLD device index value */ +enum cpld_id { + i2c_cpld +}; + +/* port number on CPLD */ +#define CPLD_1_PORT_NUM 12 +#define CPLD_2_PORT_NUM 13 + +/* QSFP port number */ +#define QSFP_MAX_PORT_NUM 64 +#define QSFP_MIN_PORT_NUM 1 + +/* SFP+ port number */ +#define SFP_MAX_PORT_NUM 2 +#define SFP_MIN_PORT_NUM 1 + + +/* CPLD registers */ +#define CPLD_BOARD_TYPE_REG 0x0 +#define CPLD_EXT_BOARD_TYPE_REG 0x7 +#define CPLD_VERSION_REG 0x1 +#define CPLD_PW_GOOD_REG 0x2 +#define CPLD_PW_ABS_REG 0x3 + + +/* bit definition for register value */ + +enum CPLD_RESET_CONTROL_BITS { + CPLD_RESET_CONTROL_SWRST_BIT, + CPLD_RESET_CONTROL_CP2104RST_BIT, + CPLD_RESET_CONTROL_82P33814RST_BIT, + CPLD_RESET_CONTROL_BMCRST_BIT, +}; + +/* bit field structure for register value */ +struct cpld_reg_board_type_t { + u8 build_rev:2; + u8 hw_rev:2; + u8 board_id:4; +}; + +struct cpld_reg_version_t { + u8 revision:6; + u8 release:1; + u8 reserve:1; +}; + +struct cpld_reg_pw_good_t { + u8 reserve1:3; + u8 psu1:1; + u8 psu2:1; + u8 reserve2:3; +}; + +struct cpld_reg_pw_abs_t { + u8 psu1:1; + u8 psu2:1; + u8 reserve:6; +}; + +/* common manipulation */ +#define INVALID(i, min, max) ((i < min) || (i > max) ? 1u : 0u) +#define READ_BIT(val, bit) ((0u == (val & (1<bf_name) +#define READ_BF_1(bf_struct, val, bf_name, bf_value) \ + bf_struct bf; \ + bf.data = val; \ + bf_value = bf.bf_name +#define BOARD_TYPE_BUILD_REV_GET(val, res) \ + READ_BF(cpld_reg_board_type_t, val, build_rev, res) +#define BOARD_TYPE_HW_REV_GET(val, res) \ + READ_BF(cpld_reg_board_type_t, val, hw_rev, res) +#define BOARD_TYPE_BOARD_ID_GET(val, res) \ + READ_BF(cpld_reg_board_type_t, val, board_id, res) +#define CPLD_VERSION_REV_GET(val, res) \ + READ_BF(cpld_reg_version_t, val, revision, res) +#define CPLD_VERSION_REL_GET(val, res) \ + READ_BF(cpld_reg_version_t, val, release, res) +#define CPLD_PSU1_PW_GOOD_GET(val, res) \ + READ_BF(cpld_reg_pw_good_t, val, psu1, res) +#define CPLD_PSU2_PW_GOOD_GET(val, res) \ + READ_BF(cpld_reg_pw_good_t, val, psu2, res) +#define CPLD_PSU1_PW_ABS_GET(val, res) \ + READ_BF(cpld_reg_pw_abs_t, val, psu1, res) +#define CPLD_PSU2_PW_ABS_GET(val, res) \ + READ_BF(cpld_reg_pw_abs_t, val, psu2, res) +/* QSFP/SFP registers manipulation */ +#define QSFP_TO_CPLD_IDX(qsfp_port, cpld_index, cpld_port) \ +{ \ + if (QSFP_MIN_PORT_NUM <= qsfp_port && qsfp_port <= CPLD_1_PORT_NUM) { \ + cpld_index = cpld1; \ + cpld_port = qsfp_port - 1; \ + } else if (CPLD_1_PORT_NUM < qsfp_port \ + && qsfp_port <= QSFP_MAX_PORT_NUM) { \ + cpld_index = cpld2 + (qsfp_port - 1 - CPLD_1_PORT_NUM) \ + / CPLD_2_PORT_NUM; \ + cpld_port = (qsfp_port - 1 - CPLD_1_PORT_NUM) % \ + CPLD_2_PORT_NUM; \ + } else { \ + cpld_index = 0; \ + cpld_port = 0; \ + } \ +} +#define SFP_TO_CPLD_IDX(sfp_port, cpld_index) \ + (cpld_index = sfp_port - SFP_MIN_PORT_NUM) +#define QSFP_PORT_STATUS_REG(cpld_port) \ + (CPLD_QSFP_PORT_STATUS_BASE_REG + cpld_port) +#define QSFP_PORT_CONFIG_REG(cpld_port) \ + (CPLD_QSFP_PORT_CONFIG_BASE_REG + cpld_port) +#define QSFP_PORT_INT_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_QSFP_PORT_STATUS_INT_BIT) +#define QSFP_PORT_ABS_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_QSFP_PORT_STATUS_ABS_BIT) +#define QSFP_PORT_RESET_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_RESET_BIT) +#define QSFP_PORT_LPMODE_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_LPMODE_BIT) +#define QSFP_PORT_RESET_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_RESET_BIT) +#define QSFP_PORT_RESET_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_RESET_BIT) +#define QSFP_PORT_LPMODE_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_LPMODE_BIT) +#define QSFP_PORT_LPMODE_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_LPMODE_BIT) +#define SFP_PORT_PRESENT_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_STATUS_PRESENT_BIT) + + #define SFP_PORT_TXFAULT_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_STATUS_TXFAULT_BIT) + #define SFP_PORT_RXLOS_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_STATUS_RXLOS_BIT) + #define SFP_PORT_TXDIS_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_CONFIG_TXDIS_BIT) +#define SFP_PORT_RS_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_RS_BIT) +#define SFP_PORT_TS_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TS_BIT) +#define SFP_PORT_TXDIS_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TXDIS_BIT) +#define SFP_PORT_TXDIS_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TXDIS_BIT) +#define SFP_PORT_RS_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_RS_BIT) +#define SFP_PORT_RS_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_RS_BIT) +#define SFP_PORT_TS_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TS_BIT) +#define SFP_PORT_TS_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TS_BIT) + +/* CPLD access functions */ +extern int i2c_cpld_get_qsfp_port_status_val(u8 port_num); +extern int i2c_cpld_get_qsfp_port_config_val(u8 port_num); +extern int i2c_cpld_set_qsfp_port_config_val(u8 port_num, u8 reg_val); +extern int i2c_cpld_get_sfp_port_status_val(u8 port_num); +extern int i2c_cpld_get_sfp_port_config_val(u8 port_num); +extern int i2c_cpld_set_sfp_port_config_val(u8 port_num, u8 reg_val); +extern u8 fp_port_to_phy_port(u8 fp_port); +#endif + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/service/qsfp-monitor.service b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/service/qsfp-monitor.service new file mode 100644 index 000000000000..e33c2fdae122 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/service/qsfp-monitor.service @@ -0,0 +1,15 @@ +[Unit] +Description= This QSFP Monitor service is to setup QSFP SI. +Requires=s8900-54xc-monitor.service +After=s8900-54xc-monitor.service + +[Service] +ExecStart=/usr/sbin/qsfp_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/service/s8900-54xc-monitor.service b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/service/s8900-54xc-monitor.service new file mode 100644 index 000000000000..e43fddc9b857 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/service/s8900-54xc-monitor.service @@ -0,0 +1,18 @@ +[Unit] +Description= This Platform Monitor service is to initialize platform and monitor platform. +Before=platform-monitor.service +After=sysinit.target +Wants=fancontrol.service +DefaultDependencies=no + +[Service] +ExecStartPre=/usr/sbin/i2c_utils.sh i2c_init +ExecStart=/usr/sbin/s8900_54xc_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/utils/i2c_utils.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/utils/i2c_utils.sh new file mode 100644 index 000000000000..eea7b9ba8fe9 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/utils/i2c_utils.sh @@ -0,0 +1,1485 @@ +#!/bin/bash + +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +VERSION="1.0.0" +TRUE=200 +FALSE=404 +PORT_START=1 +PORT_END=54 + +EXEC_FUNC=${1} +COLOR_LED=${2} +QSFP_PORT=${2} +ONOFF_LED=${3} +FAN_TRAY=${4} + +############################################################ +# Distributor ID: Debian +# Description: Debian GNU/Linux 8.6 (jessie) +# Release: 8.6 +# Codename: jessie +# Linux debian 3.16.0-4-amd64 #1 +# SMP Debian 3.16.36-1+deb8u1 (2016-09-03) x86_64 GNU/Linux +############################################################ + +# Color Definition +COLOR_TITLE="\e[1;32m" ### Green ### +COLOR_WARNING="\e[1;33m" ### Yellow ### +COLOR_ERROR="\e[1;31m" ### Red ### +COLOR_END="\e[0m" ### END ### + +NUM_IGB_DEVICE=0 +NUM_I801_DEVICE=0 +NUM_ISMT_DEVICE=$(( ${NUM_I801_DEVICE} + 1 )) +NUM_MUX1_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 2 )) +NUM_MUX1_CHAN1_DEVICE=$(( ${NUM_I801_DEVICE} + 3 )) +NUM_MUX1_CHAN2_DEVICE=$(( ${NUM_I801_DEVICE} + 4 )) +NUM_MUX1_CHAN3_DEVICE=$(( ${NUM_I801_DEVICE} + 5 )) +NUM_MUX1_CHAN4_DEVICE=$(( ${NUM_I801_DEVICE} + 6 )) +NUM_MUX1_CHAN5_DEVICE=$(( ${NUM_I801_DEVICE} + 7 )) +NUM_MUX1_CHAN6_DEVICE=$(( ${NUM_I801_DEVICE} + 8 )) +NUM_MUX1_CHAN7_DEVICE=$(( ${NUM_I801_DEVICE} + 9 )) +NUM_MUX2_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 10 )) +NUM_MUX2_CHAN1_DEVICE=$(( ${NUM_I801_DEVICE} + 11 )) +NUM_MUX2_CHAN2_DEVICE=$(( ${NUM_I801_DEVICE} + 12 )) +NUM_MUX2_CHAN3_DEVICE=$(( ${NUM_I801_DEVICE} + 13 )) +NUM_MUX2_CHAN4_DEVICE=$(( ${NUM_I801_DEVICE} + 14 )) +NUM_MUX2_CHAN5_DEVICE=$(( ${NUM_I801_DEVICE} + 15 )) +NUM_MUX2_CHAN6_DEVICE=$(( ${NUM_I801_DEVICE} + 16 )) +NUM_MUX2_CHAN7_DEVICE=$(( ${NUM_I801_DEVICE} + 17 )) +NUM_MUX3_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 18 )) +NUM_MUX4_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 26 )) +NUM_MUX5_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 34 )) +NUM_MUX6_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 42 )) +NUM_MUX7_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 50 )) +NUM_MUX8_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 58 )) +NUM_MUX9_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 66 )) + +PATH_SYS_I2C_DEVICES="/sys/bus/i2c/devices" +PATH_HWMON_ROOT_DEVICES="/sys/class/hwmon" +PATH_HWMON_W83795_DEVICE="${PATH_HWMON_ROOT_DEVICES}/hwmon1" +PATH_I801_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_I801_DEVICE}" +PATH_ISMT_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_ISMT_DEVICE}" +PATH_MUX_CHAN0_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN0_DEVICE}" +PATH_MUX_CHAN1_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN1_DEVICE}" +PATH_MUX_CHAN2_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN2_DEVICE}" +PATH_MUX_CHAN3_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN3_DEVICE}" +PATH_MUX_CHAN4_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN4_DEVICE}" +PATH_MUX_CHAN5_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN5_DEVICE}" +PATH_MUX_CHAN6_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN6_DEVICE}" +PATH_MUX_CHAN7_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN7_DEVICE}" + +#SFP/QSFP EEPROM i2c bus index +SFP_EEPROM_BUS_IDX=0 + +#Power Supply Status +PSU_DC_ON=1 +PSU_DC_OFF=0 +PSU_EXIST=1 +PSU_NOT_EXIST=0 + +#GPIO Index for QSFP +ZQSFP_PORT0_15_ABS_GPIO_IDX=192 +ZQSFP_PORT16_31_ABS_GPIO_IDX=176 +ZQSFP_PORT32_47_ABS_GPIO_IDX=160 + + +# Help usage function +function _help { + echo "=========================================================" + echo "# Description: Help Function" + echo "=========================================================" + echo "----------------------------------------------------" + echo "EX : ${0} help" + echo " : ${0} i2c_init" + echo " : ${0} i2c_deinit" + echo " : ${0} i2c_temp_init" + echo " : ${0} i2c_fan_init" + echo " : ${0} i2c_volmon_init" + echo " : ${0} i2c_io_exp_init" + echo " : ${0} i2c_gpio_init" + echo " : ${0} i2c_gpio_deinit" + echo " : ${0} i2c_led_test" + echo " : ${0} i2c_psu_eeprom_get" + echo " : ${0} i2c_mb_eeprom_get" + echo " : ${0} i2c_qsfp_eeprom_get [1-54]" + echo " : ${0} i2c_qsfp_status_get [1-54]" + echo " : ${0} i2c_qsfp_type_get [1-54]" + echo " : ${0} i2c_board_type_get" + echo " : ${0} i2c_psu_status" + echo " : ${0} i2c_led_psu_status_set" + echo " : ${0} i2c_led_fan_status_set" + echo " : ${0} i2c_led_fan_tray_status_set" + echo " : ${0} i2c_cpld_version" + echo " : ${0} i2c_front_temp" + echo " : ${0} i2c_rear_temp" + echo " : ${0} i2c_test_all" + echo " : ${0} i2c_sys_led green|amber on|off" + echo " : ${0} i2c_fan_led green|amber on|off" + echo " : ${0} i2c_psu1_led green|amber on|off" + echo " : ${0} i2c_psu2_led green|amber on|off" + echo " : ${0} i2c_fan_tray_led green|amber on|off [1-4]" + echo "----------------------------------------------------" +} + +#Pause function +function _pause { + read -p "$*" +} + +#Retry command function +function _retry { + for i in {1..5}; + do + eval "${*}" && break || echo "retry"; sleep 1; + done +} + + +#I2C Init +function _i2c_init { + echo "=========================================================" + echo "# Description: I2C Init" + echo "=========================================================" + + rmmod i2c_ismt + rmmod i2c_i801 + modprobe i2c_i801 + modprobe i2c_ismt + modprobe i2c_dev + modprobe i2c_mux_pca954x force_deselect_on_exit=1 + + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x70' > ${PATH_ISMT_DEVICE}/new_device" + else + echo "pca9548 0x70 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX2_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x72' > ${PATH_ISMT_DEVICE}/new_device" + else + echo "pca9548 0x72 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX3_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x71' > ${PATH_MUX_CHAN0_DEVICE}/new_device" + else + echo "pca9548 0x71 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX4_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x71' > ${PATH_MUX_CHAN1_DEVICE}/new_device" + else + echo "pca9548 0x71 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX5_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x71' > ${PATH_MUX_CHAN2_DEVICE}/new_device" + else + echo "pca9548 0x71 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX6_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x71' > ${PATH_MUX_CHAN3_DEVICE}/new_device" + else + echo "pca9548 0x71 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX7_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x71' > ${PATH_MUX_CHAN4_DEVICE}/new_device" + else + echo "pca9548 0x71 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX8_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x71' > ${PATH_MUX_CHAN5_DEVICE}/new_device" + else + echo "pca9548 0x71 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX9_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x71' > ${PATH_MUX_CHAN6_DEVICE}/new_device" + else + echo "pca9548 0x71 already init." + fi + + #Init CPLD LED_CLR Register (Front Port LED) + i2cset -y ${NUM_I801_DEVICE} 0x33 0x34 0x10 + + rmmod coretemp + rmmod jc42 + rmmod w83795 + _i2c_temp_init + _i2c_volmon_init + _i2c_hwmon_init + modprobe coretemp + modprobe w83795 + modprobe jc42 + modprobe sff_8436_eeprom + modprobe eeprom + modprobe eeprom_mb + _i2c_fan_init + _i2c_io_exp_init + _i2c_gpio_init + _i2c_cpld_init + _i2c_psu_eeprom_init + _i2c_led_psu_status_set + _i2c_led_fan_status_set + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_sys_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_sys_led + echo "Mount Main Board EEPROM" + echo "mb_eeprom 0x54" > /sys/bus/i2c/devices/i2c-17/new_device + for (( i=$PORT_START; i<=$PORT_END; i++ )) + do + _i2c_mount_sfp_eeprom $i + done + _config_rmem +} + +#I2C Deinit +function _i2c_deinit { + _i2c_gpio_deinit + for mod in coretemp jc42 w83795 eeprom_mb gpio-pca953x i2c_mux_pca954x i2c_ismt i2c_i801; + do + [ "$(lsmod | grep "^$mod ")" != "" ] && rmmod $mod + done +} + +#Temperature sensor Init +function _i2c_temp_init { + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x00 0x80 + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x05 0x7F + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x04 0x0A + echo "TEMP INIT Done" +} + +#FAN Init +function _i2c_fan_init { + echo -n "FAN INIT..." + if [ -e "${PATH_HWMON_W83795_DEVICE}" ]; then + echo 120 > ${PATH_HWMON_W83795_DEVICE}/device/pwm1 + echo 120 > ${PATH_HWMON_W83795_DEVICE}/device/pwm2 + echo "SUCCESS" + else + echo "FAIL" + fi + +} + +#VOLMON Init +function _i2c_volmon_init { + echo -n "VOLMON INIT..." + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x00 0x80 + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x02 0xFF + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x03 0x50 + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x04 0x0A + echo "Done" +} + +#HWMON Init +function _i2c_hwmon_init { + echo -n "HWMON INIT..." + i2cset -y ${NUM_I801_DEVICE} 0x2F 0x00 0x80 + i2cset -y ${NUM_I801_DEVICE} 0x2F 0x06 0xFF + echo "Done" +} + +#IO Expander Init +function _i2c_io_exp_init { + echo "=========================================================" + echo "# Description: I2C IO Expender Init" + echo "=========================================================" + #SMBUS0 IO_EXPENDER + i2cset -y -r ${NUM_I801_DEVICE} 0x27 4 0x00 + i2cset -y -r ${NUM_I801_DEVICE} 0x27 5 0x00 + i2cset -y -r ${NUM_I801_DEVICE} 0x27 6 0xFF + i2cset -y -r ${NUM_I801_DEVICE} 0x27 7 0xFF + + #SMBUS1 + #SFP+ ABS + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x21 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x21 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x22 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x22 5 0x00 + + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x20 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x20 7 0xFF + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x21 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x21 7 0xFF + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x22 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x22 7 0xFF + + #QSFP/ZQSFP ABS + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x23 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x23 6 0xFF + + #QSFP/ZQSFP INT + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x23 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN0_DEVICE} 0x23 7 0xFF + + #SFP+ RX_LOS + i2cset -y -r ${NUM_MUX2_CHAN1_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN1_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN1_DEVICE} 0x21 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN1_DEVICE} 0x21 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN1_DEVICE} 0x22 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN1_DEVICE} 0x22 5 0x00 + + i2cset -y -r ${NUM_MUX2_CHAN1_DEVICE} 0x20 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN1_DEVICE} 0x20 7 0xFF + i2cset -y -r ${NUM_MUX2_CHAN1_DEVICE} 0x21 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN1_DEVICE} 0x21 7 0xFF + i2cset -y -r ${NUM_MUX2_CHAN1_DEVICE} 0x22 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN1_DEVICE} 0x22 7 0xFF + + #SFP+ TX_FAULT + i2cset -y -r ${NUM_MUX2_CHAN2_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN2_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN2_DEVICE} 0x21 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN2_DEVICE} 0x21 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN2_DEVICE} 0x22 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN2_DEVICE} 0x22 5 0x00 + + i2cset -y -r ${NUM_MUX2_CHAN2_DEVICE} 0x20 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN2_DEVICE} 0x20 7 0xFF + i2cset -y -r ${NUM_MUX2_CHAN2_DEVICE} 0x21 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN2_DEVICE} 0x21 7 0xFF + i2cset -y -r ${NUM_MUX2_CHAN2_DEVICE} 0x22 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN2_DEVICE} 0x22 7 0xFF + + #SFP+ TX_RS + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x21 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x21 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x22 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x22 5 0x00 + + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x20 2 0xFF + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x20 3 0xFF + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x21 2 0xFF + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x21 3 0xFF + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x22 2 0xFF + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x22 3 0xFF + + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x20 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x20 7 0xFF + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x21 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x21 7 0xFF + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x22 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x22 7 0xFF + + #QSFP/zQSFP LPMODE + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x23 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x23 2 0x00 + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x23 6 0x00 + + #QSFP/zQSFP MODSEL + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x23 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x23 3 0x00 + i2cset -y -r ${NUM_MUX2_CHAN3_DEVICE} 0x23 7 0x00 + + #SFP+ RX_RS + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x21 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x21 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x22 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x22 5 0x00 + + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x20 2 0xFF + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x20 3 0xFF + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x21 2 0xFF + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x21 3 0xFF + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x22 2 0xFF + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x22 3 0xFF + + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x20 6 0x00 + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x20 7 0x00 + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x21 6 0x00 + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x21 7 0x00 + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x22 6 0x00 + i2cset -y -r ${NUM_MUX2_CHAN4_DEVICE} 0x22 7 0x00 + + #SFP+ TX_DIS + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x21 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x21 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x22 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x22 5 0x00 + + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x20 2 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x20 3 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x21 2 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x21 3 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x22 2 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x22 3 0x00 + + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x20 6 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x20 7 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x21 6 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x21 7 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x22 6 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x22 7 0x00 + + #QSFP/zQSFP RST + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x23 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x23 2 0xFF + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x23 6 0x00 + + #SFP/QSFP/zQSFP I/O + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x24 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x24 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x24 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN5_DEVICE} 0x24 7 0xFF + + #ZQSFP/SFP+/E-Card General + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x21 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x21 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x21 2 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x21 3 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x21 6 0xF4 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x21 7 0xF4 + + #LED board after PVT (S8900_IO_EXP_LED_ID) + echo "Init LED IO Expender" + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 6 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 7 0x00 + + #PSU I/O (S8900_IO_EXP_PSU_ID) + echo "Init PSU IO Expender" + i2cset -y -r ${NUM_MUX2_CHAN6_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN6_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN6_DEVICE} 0x20 2 0x00 + i2cset -y -r ${NUM_MUX2_CHAN6_DEVICE} 0x20 3 0x00 + i2cset -y -r ${NUM_MUX2_CHAN6_DEVICE} 0x20 6 0xFF + i2cset -y -r ${NUM_MUX2_CHAN6_DEVICE} 0x20 7 0xFF + + #FAN I/O (S8900_IO_EXP_FAN_ID) + echo "Init FAN IO Expender" + i2cset -y -r ${NUM_MUX2_CHAN7_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX2_CHAN7_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX2_CHAN7_DEVICE} 0x20 2 0x11 + i2cset -y -r ${NUM_MUX2_CHAN7_DEVICE} 0x20 3 0x11 + i2cset -y -r ${NUM_MUX2_CHAN7_DEVICE} 0x20 6 0xCC + i2cset -y -r ${NUM_MUX2_CHAN7_DEVICE} 0x20 7 0xCC +} + +function _get_sfp_eeprom_bus_idx { + case $1 in + 1|2|3|4|5|6|7|8) + SFP_EEPROM_BUS_IDX=$(( (${NUM_MUX3_CHAN0_DEVICE} + $1 - 1) )) + ;; + 9|10|11|12|13|14|15|16) + SFP_EEPROM_BUS_IDX=$(( (${NUM_MUX4_CHAN0_DEVICE} + $1 - 9) )) + ;; + 17|18|19|20|21|22|23|24) + SFP_EEPROM_BUS_IDX=$(( (${NUM_MUX5_CHAN0_DEVICE} + $1 - 17) )) + ;; + 25|26|27|28|29|30|31|32) + SFP_EEPROM_BUS_IDX=$(( (${NUM_MUX6_CHAN0_DEVICE} + $1 - 25) )) + ;; + 33|34|35|36|37|38|39|40) + SFP_EEPROM_BUS_IDX=$(( (${NUM_MUX7_CHAN0_DEVICE} + $1 - 33) )) + ;; + 41|42|43|44|45|46|47|48) + SFP_EEPROM_BUS_IDX=$(( (${NUM_MUX8_CHAN0_DEVICE} + $1 - 41) )) + ;; + 49|50|51|52|53|54) + SFP_EEPROM_BUS_IDX=$(( (${NUM_MUX9_CHAN0_DEVICE} + $1 - 49) )) + ;; + *) + SFP_EEPROM_BUS_IDX=-1 + ;; + esac +} + +#Mount SFP/QSFP EEPROM +function _i2c_mount_sfp_eeprom { + _get_sfp_eeprom_bus_idx $1 + eeprombus=${SFP_EEPROM_BUS_IDX} + eepromAddr=0x50 + echo "sff8436 $eepromAddr" > /sys/bus/i2c/devices/i2c-$eeprombus/new_device + echo "Mount Port $1 EEPROM" +} + +#Unmount SFP/QSFP EEPROM +function _i2c_unmount_sfp_eeprom { + _get_sfp_eeprom_bus_idx $1 + eeprombus=${SFP_EEPROM_BUS_IDX} + eepromAddr=0x50 + echo "$eepromAddr" > /sys/bus/i2c/devices/i2c-$eeprombus/new_device + echo "Unmount Port $1 EEPROM" +} + +#GPIO Init +function _i2c_gpio_init { + + #QSFP/ZQSFP ABS+INT + echo "pca9535 0x23" > /sys/bus/i2c/devices/i2c-${NUM_MUX2_CHAN0_DEVICE}/new_device + + _retry "echo 240 > /sys/class/gpio/export" + echo 241 > /sys/class/gpio/export + echo 242 > /sys/class/gpio/export + echo 243 > /sys/class/gpio/export + echo 244 > /sys/class/gpio/export + echo 245 > /sys/class/gpio/export + echo 246 > /sys/class/gpio/export + echo 247 > /sys/class/gpio/export + echo 248 > /sys/class/gpio/export + echo 249 > /sys/class/gpio/export + echo 250 > /sys/class/gpio/export + echo 251 > /sys/class/gpio/export + echo 252 > /sys/class/gpio/export + echo 253 > /sys/class/gpio/export + echo 254 > /sys/class/gpio/export + echo 255 > /sys/class/gpio/export + + echo 1 > /sys/class/gpio/gpio241/active_low #QSFP49 ABS + echo 1 > /sys/class/gpio/gpio240/active_low #QSFP48 ABS + echo 1 > /sys/class/gpio/gpio243/active_low #QSFP51 ABS + echo 1 > /sys/class/gpio/gpio242/active_low #QSFP50 ABS + echo 1 > /sys/class/gpio/gpio245/active_low #QSFP53 ABS + echo 1 > /sys/class/gpio/gpio244/active_low #QSFP52 ABS + echo 1 > /sys/class/gpio/gpio247/active_low #NA + echo 1 > /sys/class/gpio/gpio246/active_low #NA + echo 1 > /sys/class/gpio/gpio249/active_low #QSFP49 INT + echo 1 > /sys/class/gpio/gpio248/active_low #QSFP48 INT + echo 1 > /sys/class/gpio/gpio251/active_low #QSFP51 INT + echo 1 > /sys/class/gpio/gpio250/active_low #QSFP50 INT + echo 1 > /sys/class/gpio/gpio253/active_low #QSFP53 INT + echo 1 > /sys/class/gpio/gpio252/active_low #QSFP52 INT + echo 1 > /sys/class/gpio/gpio255/active_low #NA + echo 1 > /sys/class/gpio/gpio254/active_low #NA + + #QSFP/zQSFP LPMODE+MODSEL + echo "pca9535 0x23" > /sys/bus/i2c/devices/i2c-${NUM_MUX2_CHAN3_DEVICE}/new_device + echo 224 > /sys/class/gpio/export #QSFP0 LPMODE + echo 225 > /sys/class/gpio/export #QSFP1 LPMODE + echo 226 > /sys/class/gpio/export #QSFP2 LPMODE + echo 227 > /sys/class/gpio/export #QSFP3 LPMODE + echo 228 > /sys/class/gpio/export #QSFP4 LPMODE + echo 229 > /sys/class/gpio/export #QSFP5 LPMODE + echo 230 > /sys/class/gpio/export #NA + echo 231 > /sys/class/gpio/export #NA + echo 232 > /sys/class/gpio/export #QSFP0 MODSEL + echo 233 > /sys/class/gpio/export #QSFP1 MODSEL + echo 234 > /sys/class/gpio/export #QSFP2 MODSEL + echo 235 > /sys/class/gpio/export #QSFP3 MODSEL + echo 236 > /sys/class/gpio/export #QSFP4 MODSEL + echo 237 > /sys/class/gpio/export #QSFP5 MODSEL + echo 238 > /sys/class/gpio/export #NA + echo 239 > /sys/class/gpio/export #NA + echo out > /sys/class/gpio/gpio224/direction + echo out > /sys/class/gpio/gpio225/direction + echo out > /sys/class/gpio/gpio226/direction + echo out > /sys/class/gpio/gpio227/direction + echo out > /sys/class/gpio/gpio228/direction + echo out > /sys/class/gpio/gpio229/direction + echo out > /sys/class/gpio/gpio230/direction + echo out > /sys/class/gpio/gpio231/direction + echo out > /sys/class/gpio/gpio232/direction + echo out > /sys/class/gpio/gpio233/direction + echo out > /sys/class/gpio/gpio234/direction + echo out > /sys/class/gpio/gpio235/direction + echo out > /sys/class/gpio/gpio236/direction + echo out > /sys/class/gpio/gpio237/direction + echo out > /sys/class/gpio/gpio238/direction + echo out > /sys/class/gpio/gpio239/direction + + #QSFP RST + echo "pca9535 0x23" > /sys/bus/i2c/devices/i2c-${NUM_MUX2_CHAN5_DEVICE}/new_device + echo 208 > /sys/class/gpio/export #QSFP0 RST + echo 209 > /sys/class/gpio/export #QSFP1 RST + echo 210 > /sys/class/gpio/export #QSFP2 RST + echo 211 > /sys/class/gpio/export #QSFP3 RST + echo 212 > /sys/class/gpio/export #QSFP4 RST + echo 213 > /sys/class/gpio/export #QSFP5 RST + echo 214 > /sys/class/gpio/export #NA + echo 215 > /sys/class/gpio/export #NA + echo 216 > /sys/class/gpio/export #NA + echo 217 > /sys/class/gpio/export #NA + echo 218 > /sys/class/gpio/export #NA + echo 219 > /sys/class/gpio/export #NA + echo 220 > /sys/class/gpio/export #NA + echo 221 > /sys/class/gpio/export #NA + echo 222 > /sys/class/gpio/export #NA + echo 223 > /sys/class/gpio/export #NA + echo out > /sys/class/gpio/gpio208/direction + echo out > /sys/class/gpio/gpio209/direction + echo out > /sys/class/gpio/gpio210/direction + echo out > /sys/class/gpio/gpio211/direction + echo out > /sys/class/gpio/gpio212/direction + echo out > /sys/class/gpio/gpio213/direction + echo 1 > /sys/class/gpio/gpio208/active_low + echo 1 > /sys/class/gpio/gpio209/active_low + echo 1 > /sys/class/gpio/gpio210/active_low + echo 1 > /sys/class/gpio/gpio211/active_low + echo 1 > /sys/class/gpio/gpio212/active_low + echo 1 > /sys/class/gpio/gpio213/active_low + echo 0 > /sys/class/gpio/gpio208/value + echo 0 > /sys/class/gpio/gpio209/value + echo 0 > /sys/class/gpio/gpio210/value + echo 0 > /sys/class/gpio/gpio211/value + echo 0 > /sys/class/gpio/gpio212/value + echo 0 > /sys/class/gpio/gpio213/value + + #SFP+ ABS 0-15 + echo "pca9535 0x20" > /sys/bus/i2c/devices/i2c-${NUM_MUX2_CHAN0_DEVICE}/new_device + echo 192 > /sys/class/gpio/export + echo 193 > /sys/class/gpio/export + echo 194 > /sys/class/gpio/export + echo 195 > /sys/class/gpio/export + echo 196 > /sys/class/gpio/export + echo 197 > /sys/class/gpio/export + echo 198 > /sys/class/gpio/export + echo 199 > /sys/class/gpio/export + echo 200 > /sys/class/gpio/export + echo 201 > /sys/class/gpio/export + echo 202 > /sys/class/gpio/export + echo 203 > /sys/class/gpio/export + echo 204 > /sys/class/gpio/export + echo 205 > /sys/class/gpio/export + echo 206 > /sys/class/gpio/export + echo 207 > /sys/class/gpio/export + echo 1 > /sys/class/gpio/gpio192/active_low #SFP+00 + echo 1 > /sys/class/gpio/gpio193/active_low #SFP+01 + echo 1 > /sys/class/gpio/gpio194/active_low #SFP+02 + echo 1 > /sys/class/gpio/gpio195/active_low #SFP+03 + echo 1 > /sys/class/gpio/gpio196/active_low #SFP+04 + echo 1 > /sys/class/gpio/gpio197/active_low #SFP+05 + echo 1 > /sys/class/gpio/gpio198/active_low #SFP+06 + echo 1 > /sys/class/gpio/gpio199/active_low #SFP+07 + echo 1 > /sys/class/gpio/gpio200/active_low #SFP+08 + echo 1 > /sys/class/gpio/gpio201/active_low #SFP+09 + echo 1 > /sys/class/gpio/gpio202/active_low #SFP+10 + echo 1 > /sys/class/gpio/gpio203/active_low #SFP+11 + echo 1 > /sys/class/gpio/gpio204/active_low #SFP+12 + echo 1 > /sys/class/gpio/gpio205/active_low #SFP+13 + echo 1 > /sys/class/gpio/gpio206/active_low #SFP+14 + echo 1 > /sys/class/gpio/gpio207/active_low #SFP+15 + + #SFP+ ABS 16-31 + echo "pca9535 0x21" > /sys/bus/i2c/devices/i2c-${NUM_MUX2_CHAN0_DEVICE}/new_device + echo 176 > /sys/class/gpio/export + echo 177 > /sys/class/gpio/export + echo 178 > /sys/class/gpio/export + echo 179 > /sys/class/gpio/export + echo 180 > /sys/class/gpio/export + echo 181 > /sys/class/gpio/export + echo 182 > /sys/class/gpio/export + echo 183 > /sys/class/gpio/export + echo 184 > /sys/class/gpio/export + echo 185 > /sys/class/gpio/export + echo 186 > /sys/class/gpio/export + echo 187 > /sys/class/gpio/export + echo 188 > /sys/class/gpio/export + echo 189 > /sys/class/gpio/export + echo 190 > /sys/class/gpio/export + echo 191 > /sys/class/gpio/export + echo 1 > /sys/class/gpio/gpio176/active_low #SFP+16 + echo 1 > /sys/class/gpio/gpio177/active_low #SFP+17 + echo 1 > /sys/class/gpio/gpio178/active_low #SFP+18 + echo 1 > /sys/class/gpio/gpio179/active_low #SFP+19 + echo 1 > /sys/class/gpio/gpio180/active_low #SFP+20 + echo 1 > /sys/class/gpio/gpio181/active_low #SFP+21 + echo 1 > /sys/class/gpio/gpio182/active_low #SFP+22 + echo 1 > /sys/class/gpio/gpio183/active_low #SFP+23 + echo 1 > /sys/class/gpio/gpio184/active_low #SFP+24 + echo 1 > /sys/class/gpio/gpio185/active_low #SFP+25 + echo 1 > /sys/class/gpio/gpio186/active_low #SFP+26 + echo 1 > /sys/class/gpio/gpio187/active_low #SFP+27 + echo 1 > /sys/class/gpio/gpio188/active_low #SFP+28 + echo 1 > /sys/class/gpio/gpio189/active_low #SFP+29 + echo 1 > /sys/class/gpio/gpio190/active_low #SFP+30 + echo 1 > /sys/class/gpio/gpio191/active_low #SFP+31 + + #SFP+ ABS 32-47 + echo "pca9535 0x22" > /sys/bus/i2c/devices/i2c-${NUM_MUX2_CHAN0_DEVICE}/new_device + echo 160 > /sys/class/gpio/export + echo 161 > /sys/class/gpio/export + echo 162 > /sys/class/gpio/export + echo 163 > /sys/class/gpio/export + echo 164 > /sys/class/gpio/export + echo 165 > /sys/class/gpio/export + echo 166 > /sys/class/gpio/export + echo 167 > /sys/class/gpio/export + echo 168 > /sys/class/gpio/export + echo 169 > /sys/class/gpio/export + echo 170 > /sys/class/gpio/export + echo 171 > /sys/class/gpio/export + echo 172 > /sys/class/gpio/export + echo 173 > /sys/class/gpio/export + echo 174 > /sys/class/gpio/export + echo 175 > /sys/class/gpio/export + echo 1 > /sys/class/gpio/gpio160/active_low #SFP+32 + echo 1 > /sys/class/gpio/gpio161/active_low #SFP+33 + echo 1 > /sys/class/gpio/gpio162/active_low #SFP+34 + echo 1 > /sys/class/gpio/gpio163/active_low #SFP+35 + echo 1 > /sys/class/gpio/gpio164/active_low #SFP+36 + echo 1 > /sys/class/gpio/gpio165/active_low #SFP+37 + echo 1 > /sys/class/gpio/gpio166/active_low #SFP+38 + echo 1 > /sys/class/gpio/gpio167/active_low #SFP+39 + echo 1 > /sys/class/gpio/gpio168/active_low #SFP+40 + echo 1 > /sys/class/gpio/gpio169/active_low #SFP+41 + echo 1 > /sys/class/gpio/gpio170/active_low #SFP+42 + echo 1 > /sys/class/gpio/gpio171/active_low #SFP+43 + echo 1 > /sys/class/gpio/gpio172/active_low #SFP+44 + echo 1 > /sys/class/gpio/gpio173/active_low #SFP+45 + echo 1 > /sys/class/gpio/gpio174/active_low #SFP+46 + echo 1 > /sys/class/gpio/gpio175/active_low #SFP+47 + +} + +#GPIO DeInit +function _i2c_gpio_deinit { + echo "0x23" > /sys/bus/i2c/devices/i2c-${NUM_MUX2_CHAN0_DEVICE}/delete_device + echo "0x23" > /sys/bus/i2c/devices/i2c-${NUM_MUX2_CHAN3_DEVICE}/delete_device + echo "0x23" > /sys/bus/i2c/devices/i2c-${NUM_MUX2_CHAN5_DEVICE}/delete_device + echo "0x20" > /sys/bus/i2c/devices/i2c-${NUM_MUX2_CHAN0_DEVICE}/delete_device + echo "0x21" > /sys/bus/i2c/devices/i2c-${NUM_MUX2_CHAN0_DEVICE}/delete_device + echo "0x22" > /sys/bus/i2c/devices/i2c-${NUM_MUX2_CHAN0_DEVICE}/delete_device +} + +#I2C CPLD init +function _i2c_cpld_init { + echo "=========================================================" + echo "# Description: I2C CPLD Init..." + echo "=========================================================" + + ## modprobe i2c_cpld + modprobe i2c_cpld + ## Add CPLD device + echo "i2c_cpld 0x33" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_I801_DEVICE}/new_device + + echo "done..." +} + +#Set FAN Tray LED +function _i2c_led_fan_tray_status_set { + echo "FAN Tray Status Setup" + #FAN Status get + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN2_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan2_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN4_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan4_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN6_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan6_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + FAN8_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan8_alarm` + + if [ "${FAN1_ALARM}" == "0" ] && [ "${FAN2_ALARM}" == "0" ]; then + FAN_TRAY=1 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=1 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + + if [ "${FAN3_ALARM}" == "0" ] && [ "${FAN4_ALARM}" == "0" ]; then + FAN_TRAY=2 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=2 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + + if [ "${FAN5_ALARM}" == "0" ] && [ "${FAN6_ALARM}" == "0" ]; then + FAN_TRAY=3 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=3 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + + if [ "${FAN7_ALARM}" == "0" ] && [ "${FAN8_ALARM}" == "0" ]; then + FAN_TRAY=4 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=4 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi +} + +#Set FAN LED +function _i2c_led_fan_status_set { + echo "FAN Status Setup" + #PSU Status set + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN2_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan2_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN4_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan4_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN6_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan6_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + FAN8_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan8_alarm` + + if [ "${FAN1_ALARM}" == "0" ] && [ "${FAN2_ALARM}" == "0" ] \ + && [ "${FAN3_ALARM}" == "0" ] && [ "${FAN4_ALARM}" == "0" ] \ + && [ "${FAN5_ALARM}" == "0" ] && [ "${FAN6_ALARM}" == "0" ] \ + && [ "${FAN7_ALARM}" == "0" ] && [ "${FAN8_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + fi +} + +#Set Power Supply LED +function _i2c_led_psu_status_set { + echo "PSU LED Status Setup" + + #PSU Status set + _i2c_psu_status + + #PSU1 Status + if [ "${psu1Exist}" == ${PSU_EXIST} ]; then + if [ "${psu1PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + fi + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + fi + + #PSU2 Status + if [ "${psu2Exist}" == ${PSU_EXIST} ]; then + if [ "${psu2PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + fi + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + fi +} + +#LED Test +function _i2c_led_test { + echo "=========================================================" + echo "# Description: I2C LED TEST..." + echo "=========================================================" + #sys led (green) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x7F + _pause 'Check SYS LED green light and Press [Enter] key to continue...' + #sys led (amber) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xBF + _pause 'Check SYS LED amber light and Press [Enter] key to continue...' + + #FAN led (green) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xF7 + _pause 'Check FAN LED green light and Press [Enter] key to continue...' + #FAN led (amber) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFB + _pause 'Check FAN LED amber light and Press [Enter] key to continue...' + + #PSU2 led (green) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xDF + _pause 'Check PSU2 LED green light and Press [Enter] key to continue...' + #PSU2 led (amber) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xEF + _pause 'Check PSU2 LED amber light and Press [Enter] key to continue...' + + #PSU1 led (green) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFD + _pause 'Check PSU1 LED green light and Press [Enter] key to continue...' + #PSU1 led (amber) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFE + _pause 'Check PSU1 LED amber light and Press [Enter] key to continue...' + + #Turn OFF All LED + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + _pause 'Check turn off all LEDs and Press [Enter] key to continue...' + echo "done..." +} + +#Set QSFP Port variable +function _qsfp_port_i2c_var_set { + local port=$1 + case ${port} in + 1|2|3|4|5|6|7|8) + i2cbus=${NUM_MUX2_CHAN0_DEVICE} + regAddr=0x20 + dataAddr=0 + gpioBase=${ZQSFP_PORT0_15_ABS_GPIO_IDX} + ;; + 9|10|11|12|13|14|15|16) + i2cbus=${NUM_MUX2_CHAN0_DEVICE} + regAddr=0x20 + dataAddr=1 + gpioBase=${ZQSFP_PORT0_15_ABS_GPIO_IDX} + ;; + 17|18|19|20|21|22|23|24) + i2cbus=${NUM_MUX2_CHAN0_DEVICE} + regAddr=0x21 + dataAddr=0 + gpioBase=${ZQSFP_PORT16_31_ABS_GPIO_IDX} + ;; + 25|26|27|28|29|30|31|32) + i2cbus=${NUM_MUX2_CHAN0_DEVICE} + regAddr=0x21 + dataAddr=1 + gpioBase=${ZQSFP_PORT16_31_ABS_GPIO_IDX} + ;; + 33|34|35|36|37|38|39|40) + i2cbus=${NUM_MUX2_CHAN0_DEVICE} + regAddr=0x22 + dataAddr=0 + gpioBase=${ZQSFP_PORT32_47_ABS_GPIO_IDX} + ;; + 41|42|43|44|45|46|47|48) + i2cbus=${NUM_MUX2_CHAN0_DEVICE} + regAddr=0x22 + dataAddr=1 + gpioBase=${ZQSFP_PORT32_47_ABS_GPIO_IDX} + ;; + 49|50|51|52|53|54) + i2cbus=${NUM_MUX2_CHAN0_DEVICE} + regAddr=0x23 + dataAddr=0 + gpioBase=$((240 - 48)) + ;; + *) + echo "Please input 1~54" + exit ${FALSE} + ;; + esac +} + +#Get QSFP present +function _i2c_qsfp_status_get { + local status + _qsfp_port_i2c_var_set ${QSFP_PORT} + if [ ${QSFP_PORT} -lt 49 ] && [ ${QSFP_PORT} -gt 0 ]; then + status=`cat /sys/class/gpio/gpio$(( $(($gpioBase + (${QSFP_PORT} - 1) % 16 )) ))/value` + elif [ ${QSFP_PORT} -ge 49 ] && [ ${QSFP_PORT} -le 54 ]; then + status=`cat /sys/class/gpio/gpio$(( $(($gpioBase + (${QSFP_PORT} - 1))) ))/value` + fi + + echo "status=$status" +} + +#Get QSFP type +function _i2c_qsfp_type_get { + _get_sfp_eeprom_bus_idx ${QSFP_PORT} + eeprombus=${SFP_EEPROM_BUS_IDX} + eepromAddr=0x50 + qsfp_info=$(base64 ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/eeprom) + + if [ ${QSFP_PORT} -ge 1 ] && [ ${QSFP_PORT} -le 48 ]; then + echo "sfp" + # 1~48 port is sfp port + identifier=$(echo $qsfp_info | base64 -d -i | hexdump -s 0 -n 1 -e '"%x"') + connector=$(echo $qsfp_info | base64 -d -i | hexdump -s 2 -n 1 -e '"%x"') + transceiver=$(echo $qsfp_info | base64 -d -i | hexdump -s 3 -n 1 -e '"%x"') + else + echo "qsfp" + # 49~54 port is qsfp port + identifier=$(echo $qsfp_info | base64 -d -i | hexdump -s 128 -n 1 -e '"%x"') + connector=$(echo $qsfp_info | base64 -d -i | hexdump -s 130 -n 1 -e '"%x"') + transceiver=$(echo $qsfp_info | base64 -d -i | hexdump -s 131 -n 1 -e '"%x"') + fi + + echo "identifier=$identifier" + echo "connector=$connector" + echo "transceiver=$transceiver" +} + +#Get QSFP EEPROM Information +function _i2c_qsfp_eeprom_get { + + _qsfp_port_i2c_var_set ${QSFP_PORT} + + if [ ${QSFP_PORT} -lt 49 ] && [ ${QSFP_PORT} -gt 0 ]; then + + #status: 0 -> Down, 1 -> Up + status=`cat /sys/class/gpio/gpio$(( $(($gpioBase + (${QSFP_PORT} - 1) % 16 )) ))/value` + echo $status + + if [ $status = 0 ]; then + exit + fi + + _get_sfp_eeprom_bus_idx ${QSFP_PORT} + eeprombus=${SFP_EEPROM_BUS_IDX} + eepromAddr=0x50 + cat ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/eeprom | hexdump -C + elif [ ${QSFP_PORT} -ge 49 ] && [ ${QSFP_PORT} -le 54 ]; then + #status: 0 -> Down, 1 -> Up + status=`cat /sys/class/gpio/gpio$(( $(($gpioBase + (${QSFP_PORT} - 1))) ))/value` + echo $status + + if [ $status = 0 ]; then + exit + fi + _get_sfp_eeprom_bus_idx ${QSFP_PORT} + eeprombus=${SFP_EEPROM_BUS_IDX} + eepromAddr=0x50 + cat ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/eeprom | hexdump -C + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi +} + +#PSU EEPROM init +function _i2c_psu_eeprom_init { + echo "=========================================================" + echo "# Description: I2C PSU EEPROM Init..." + echo "=========================================================" + + ## modprobe eeprom + modprobe eeprom + ## PUS(0) EEPROM + echo "eeprom 0x50" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX2_CHAN6_DEVICE}/new_device + + ## PUS(1) EEPROM + echo "eeprom 0x50" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX2_CHAN7_DEVICE}/new_device + + echo "done..." +} + +#Get PSU EEPROM Information +function _i2c_psu_eeprom_get { + echo "=========================================================" + echo "# Description: I2C PSU EEPROM Get..." + echo "=========================================================" + + ## modprobe eeprom + modprobe eeprom + ## PUS(0) EEPROM + cat ${PATH_SYS_I2C_DEVICES}/${NUM_MUX2_CHAN6_DEVICE}-0050/eeprom | hexdump -C + + ## PUS(1) EEPROM + cat ${PATH_SYS_I2C_DEVICES}/${NUM_MUX2_CHAN7_DEVICE}-0050/eeprom | hexdump -C + + echo "done..." +} + +#Get MotherBoard EEPROM Information +function _i2c_mb_eeprom_get { + echo "=========================================================" + echo "# Description: I2C MB EEPROM Get..." + echo "=========================================================" + + ## modprobe eeprom + modprobe eeprom_mb + + ## MB EEPROM + cat ${PATH_SYS_I2C_DEVICES}/${NUM_MUX2_CHAN7_DEVICE}-0054/eeprom | hexdump -C + echo "done..." +} + +#Set System Status LED +function _i2c_sys_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x80 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x80 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x40 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x40 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Set PSU2 LED +function _i2c_psu2_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x20 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x20 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x10 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x10 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Set FAN Tray LED +function _i2c_fan_tray_led { + case ${FAN_TRAY} in + 1) + i2cAddr=0x20 + ioPort=2 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x01 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x02 + fi + ;; + 2) + i2cAddr=0x20 + ioPort=2 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + 3) + i2cAddr=0x20 + ioPort=3 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x01 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x02 + fi + ;; + 4) + i2cAddr=0x20 + ioPort=3 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + *) + echo "Please input 1~4" + exit + ;; + esac + + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m $mask -y -r ${NUM_MUX2_CHAN7_DEVICE} $i2cAddr $ioPort 0x33 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m $mask -y -r ${NUM_MUX2_CHAN7_DEVICE} $i2cAddr $ioPort 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m $mask -y -r ${NUM_MUX2_CHAN7_DEVICE} $i2cAddr $ioPort 0x33 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m $mask -y -r ${NUM_MUX2_CHAN7_DEVICE} $i2cAddr $ioPort 0x00 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Set FAN LED +function _i2c_fan_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x08 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x08 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x04 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x04 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Set PSU1 LED +function _i2c_psu1_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x02 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x02 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x01 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x01 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Get Board Version and Type +function _i2c_board_type_get { + boardType=`cat ${PATH_SYS_I2C_DEVICES}/0-0033/cpld_board_type` + boardBuildRev=$((($boardType) & 0x03)) + boardHwRev=$((($boardType) >> 2 & 0x03)) + boardId=$((($boardType) >> 4)) + printf "BOARD_ID is 0x%02x, HW Rev %d, Build Rev %d\n" $boardId $boardHwRev $boardBuildRev + +} + +#Get CPLD Version +function _i2c_cpld_version { + cpldRev=`cat ${PATH_SYS_I2C_DEVICES}/0-0033/cpld_version` + cpldRelease=$((($cpldRev) >> 6 & 0x01)) + cpldVersion=$((($cpldRev) & 0x3F)) + printf "CPLD is %s version(0:RD 1:Release), Revision is 0x%02x\n" $cpldRelease $cpldVersion + +} + +#Get PSU Status +function _i2c_psu_status { + psuPresent=`cat ${PATH_SYS_I2C_DEVICES}/0-0033/cpld_pw_abs` + psu1Exist=$(($((($psuPresent) & 0x01))?0:1)) + psu2Exist=$(($((($psuPresent) & 0x02))?0:1)) + psuPwGood=`cat ${PATH_SYS_I2C_DEVICES}/0-0033/cpld_pw_good` + psu1PwGood=$(($((($psuPwGood) >> 3 & 0x01))?1:0)) + psu2PwGood=$(($((($psuPwGood) >> 3 & 0x02))?1:0)) + printf "PSU1 Exist:%d PSU1 PW Good:%d\n" $psu1Exist $psu1PwGood + printf "PSU2 Exist:%d PSU2 PW Good:%d\n" $psu2Exist $psu2PwGood +} + +#Get Front Sensor Temperature +function _i2c_front_temp { + #Front MAC + sensors | grep 'Front MAC Temp' -A 1 +} + +#Get Rear Sensor Temperature +function _i2c_rear_temp { + #Rear MAC + sensors | grep 'Rear MAC Temp' -A 1 +} + +#Increase read socket buffer for CoPP Test +function _config_rmem { + echo "109430400" > /proc/sys/net/core/rmem_max +} + +#Main Function +function _main { + tart_time_str=`date` + start_time_sec=$(date +%s) + + if [ "${EXEC_FUNC}" == "help" ]; then + _help + elif [ "${EXEC_FUNC}" == "i2c_init" ]; then + _i2c_init + elif [ "${EXEC_FUNC}" == "i2c_deinit" ]; then + _i2c_deinit + elif [ "${EXEC_FUNC}" == "i2c_temp_init" ]; then + _i2c_temp_init + elif [ "${EXEC_FUNC}" == "i2c_fan_init" ]; then + _i2c_fan_init + elif [ "${EXEC_FUNC}" == "i2c_volmon_init" ]; then + _i2c_volmon_init + elif [ "${EXEC_FUNC}" == "i2c_io_exp_init" ]; then + _i2c_io_exp_init + elif [ "${EXEC_FUNC}" == "i2c_gpio_init" ]; then + _i2c_gpio_init + elif [ "${EXEC_FUNC}" == "i2c_gpio_deinit" ]; then + _i2c_gpio_deinit + elif [ "${EXEC_FUNC}" == "i2c_led_test" ]; then + _i2c_led_test + elif [ "${EXEC_FUNC}" == "i2c_mb_eeprom_get" ]; then + _i2c_mb_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_psu_eeprom_get" ]; then + _i2c_psu_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_eeprom_get" ]; then + _i2c_qsfp_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_status_get" ]; then + _i2c_qsfp_status_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_type_get" ]; then + _i2c_qsfp_type_get + elif [ "${EXEC_FUNC}" == "i2c_led_psu_status_set" ]; then + _i2c_led_psu_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_status_set" ]; then + _i2c_led_fan_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_tray_status_set" ]; then + _i2c_led_fan_tray_status_set + elif [ "${EXEC_FUNC}" == "i2c_sys_led" ]; then + _i2c_sys_led + elif [ "${EXEC_FUNC}" == "i2c_fan_led" ]; then + _i2c_fan_led + elif [ "${EXEC_FUNC}" == "i2c_fan_tray_led" ]; then + _i2c_fan_tray_led + elif [ "${EXEC_FUNC}" == "i2c_psu1_led" ]; then + _i2c_psu1_led + elif [ "${EXEC_FUNC}" == "i2c_psu2_led" ]; then + _i2c_psu2_led + elif [ "${EXEC_FUNC}" == "i2c_board_type_get" ]; then + _i2c_board_type_get + elif [ "${EXEC_FUNC}" == "i2c_cpld_version" ]; then + _i2c_cpld_version + elif [ "${EXEC_FUNC}" == "i2c_psu_status" ]; then + _i2c_psu_status + elif [ "${EXEC_FUNC}" == "i2c_front_temp" ]; then + _i2c_front_temp + elif [ "${EXEC_FUNC}" == "i2c_rear_temp" ]; then + _i2c_rear_temp + elif [ "${EXEC_FUNC}" == "i2c_test_all" ]; then + _i2c_init + _i2c_temp_init + _i2c_fan_init + _i2c_io_exp_init + _i2c_led_test + _i2c_psu_eeprom_get + _i2c_mb_eeprom_get + _i2c_board_type_get + _i2c_cpld_version + _i2c_psu_status + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + end_time_str=`date` + end_time_sec=$(date +%s) + diff_time=$[ ${end_time_sec} - ${start_time_sec} ] + echo "Start Time: ${start_time_str} (${start_time_sec})" + echo "End Time : ${end_time_str} (${end_time_sec})" + echo "Total Execution Time: ${diff_time} sec" + + echo "done!!!" +} + +_main diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/utils/qsfp_monitor.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/utils/qsfp_monitor.sh new file mode 100644 index 000000000000..7f50d137bcb7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/utils/qsfp_monitor.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# Copyright (C) 2017 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=3 +I2C_UTILS="/usr/sbin/i2c_utils.sh" +QSFP_SI_SCRIPT="/usr/sbin/qsfp_si_cfg.sh" +QSFP_ARRAY=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \ + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) + +#QSFP SI monitor +function _qsfp_si_monitor { + local i + local status + for i in {0..53}; + do + status=`${I2C_UTILS} i2c_qsfp_status_get $(expr $i + 1) | egrep '^status=.*$' | sed -e 's/status=//g'` + if [ "${status}" == "1" ]; then + _qsfp_type_check $i + fi + done +} + +#QSFP type +function _qsfp_type_check { + local port=$1 + local qsfp_type=`${I2C_UTILS} i2c_qsfp_type_get $(expr $port + 1)` + local identifier=`echo "$qsfp_type" | grep '^identifier=.*$' | sed -e 's/identifier=//g'` + # identifier 11 for qsfp, 3 for sfp + if [ "${identifier}" == "11" ] || [ "${identifier}" == "3" ]; then + connector=`echo "$qsfp_type" | grep '^connector=.*$' | sed -e 's/connector=//g'` + case ${connector} in + 21|23) + #DAC + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to DAC" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} dac $port >/dev/null + fi + ;; + *) + #AOC + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to AOC" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} aoc $port >/dev/null + fi + ;; + esac + fi +} + +#Docker exist check +function _docker_swss_check { + while true + do + # Check if syncd starts + result=`docker exec -i swss bash -c "echo -en \"SELECT 1\\nHLEN HIDDEN\" | redis-cli | sed -n 2p"` #TBD FIX ME + if [ "$result" == "3" ]; then + return + fi + sleep $INTERVAL + done +} + +#Docker exist check +function _qsfp_si_cfg_script_check { + + if [ -f ${QSFP_SI_SCRIPT} ] && [ -x ${QSFP_SI_SCRIPT} ]; then + echo "SI Script exists. Start monitor." + return + else + echo "SI Script not exist. Exit monitor." + exit + fi +} + +# main function +function _main { + #Check SI Script + _qsfp_si_cfg_script_check + #Check docker swss is running + _docker_swss_check + while true + do + _qsfp_si_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/utils/qsfp_si_cfg.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/utils/qsfp_si_cfg.sh new file mode 100644 index 000000000000..6ded3e522bce --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/utils/qsfp_si_cfg.sh @@ -0,0 +1,331 @@ +#!/bin/bash +# Copyright (C) 2017 Ingrasys, Inc. +# +# 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 3 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, see . + +VERSION="1.0.0" +TRUE=200 +FALSE=404 + +TYPE=${1} +PORT=${2} + +# port number +MIN_PORT=0 +MAX_PORT=53 + +# INDEX of value field in array +SI_PRE_IDX=1 +SI_POST_IDX=2 +SI_MAIN_IDX=3 +SI_AMP_IDX=4 +SI_DRI_IDX=5 + +# type string +AOC_TYPE="aoc" +DAC_TYPE="dac" + +# si value for bcm command +SI_VAL="" + +# "PRE POST MAIN AMP DRIVERMODE" for each port in array +# TODO: need to change aoc si value when available +xe_aoc_si_value=( + "0x00 0x3e 0x32 0xf 0x0" #port 0 + "0x00 0x3a 0x36 0xf 0x0" #port 1 + "0x00 0x3a 0x36 0xf 0x0" #port 2 + "0x00 0x38 0x38 0xf 0x0" #port 3 + "0x00 0x37 0x39 0xf 0x0" #port 4 + "0x00 0x2e 0x42 0xf 0x0" #port 5 + "0x00 0x33 0x3d 0xf 0x0" #port 6 + "0x00 0x30 0x40 0xc 0x0" #port 7 + "0x00 0x2f 0x43 0xe 0x0" #port 8 + "0x00 0x2d 0x43 0xc 0x0" #port 9 + "0x00 0x2c 0x44 0xc 0x0" #port 10 + "0x00 0x2d 0x43 0xc 0x0" #port 11 + "0x00 0x29 0x47 0xc 0x0" #port 12 + "0x00 0x28 0x48 0xc 0x0" #port 13 + "0x00 0x29 0x47 0xa 0x0" #port 14 + "0x00 0x29 0x47 0xf 0x0" #port 15 + "0x00 0x18 0x58 0x9 0x0" #port 16 + "0x00 0x1d 0x53 0x9 0x0" #port 17 + "0x00 0x1c 0x54 0x9 0x0" #port 18 + "0x00 0x1c 0x54 0x9 0x0" #port 19 + "0x00 0x1c 0x54 0x9 0x0" #port 20 + "0x00 0x1c 0x54 0x9 0x0" #port 21 + "0x00 0x1c 0x54 0x9 0x0" #port 22 + "0x00 0x1c 0x54 0x9 0x0" #port 23 + "0x00 0x1c 0x54 0x8 0x0" #port 24 + "0x00 0x1c 0x54 0x8 0x0" #port 25 + "0x00 0x18 0x58 0x8 0x0" #port 26 + "0x00 0x18 0x58 0x8 0x0" #port 27 + "0x00 0x14 0x5c 0x8 0x0" #port 28 + "0x00 0x19 0x57 0x8 0x0" #port 29 + "0x00 0x19 0x57 0x8 0x0" #port 30 + "0x00 0x19 0x57 0x8 0x0" #port 31 + "0x00 0x19 0x57 0xc 0x0" #port 32 + "0x00 0x20 0x50 0x9 0x0" #port 33 + "0x00 0x21 0x4f 0xc 0x0" #port 34 + "0x00 0x24 0x4c 0xc 0x0" #port 35 + "0x00 0x24 0x4c 0xc 0x0" #port 36 + "0x00 0x24 0x4c 0xc 0x0" #port 37 + "0x00 0x24 0x4c 0xc 0x0" #port 38 + "0x00 0x24 0x4c 0xc 0x0" #port 39 + "0x00 0x28 0x48 0xd 0x0" #port 40 + "0x00 0x28 0x48 0xc 0x0" #port 41 + "0x00 0x29 0x47 0xe 0x0" #port 42 + "0x00 0x29 0x47 0xe 0x0" #port 43 + "0x00 0x29 0x47 0xe 0x0" #port 44 + "0x00 0x28 0x48 0xf 0x0" #port 45 + "0x00 0x28 0x48 0xf 0x0" #port 46 + "0x00 0x30 0x40 0xf 0x0" #port 47 +) + +# TODO: need to change aoc si value when available +ce_aoc_si_value=( + "0x00 0x28 0x48 0xc 0x0" #port 0 lane 0 + "0x00 0x29 0x41 0xc 0x0" #port 0 lane 1 + "0x00 0x29 0x41 0xf 0x0" #port 0 lane 2 + "0x00 0x29 0x41 0xc 0x0" #port 0 lane 3 + "0x00 0x28 0x42 0xc 0x0" #port 1 lane 0 + "0x00 0x3a 0x36 0xf 0x0" #port 1 lane 1 + "0x00 0x2c 0x44 0xc 0x0" #port 1 lane 2 + "0x00 0x2c 0x44 0xc 0x0" #port 1 lane 3 + "0x00 0x2f 0x41 0xc 0x0" #port 2 lane 0 + "0x00 0x38 0x38 0xc 0x0" #port 2 lane 1 + "0x00 0x2f 0x41 0xc 0x0" #port 2 lane 2 + "0x00 0x30 0x40 0xc 0x0" #port 2 lane 3 + "0x00 0x30 0x40 0xc 0x0" #port 3 lane 0 + "0x00 0x3f 0x31 0xf 0x0" #port 3 lane 1 + "0x00 0x31 0x3f 0xc 0x0" #port 3 lane 2 + "0x00 0x30 0x40 0xa 0x0" #port 3 lane 3 + "0x00 0x30 0x40 0xc 0x0" #port 4 lane 0 + "0x00 0x31 0x3f 0xf 0x0" #port 4 lane 1 + "0x00 0x2f 0x41 0xc 0x0" #port 4 lane 2 + "0x00 0x2e 0x42 0xc 0x0" #port 4 lane 3 + "0x00 0x30 0x40 0xe 0x0" #port 5 lane 0 + "0x00 0x3f 0x31 0xf 0x0" #port 5 lane 1 + "0x00 0x38 0x38 0xf 0x0" #port 5 lane 2 + "0x00 0x3f 0x31 0xf 0x0" #port 5 lane 3 +) + +xe_dac_si_value=( + "0x00 0x3e 0x32 0xf 0x0" #port 0 + "0x00 0x3a 0x36 0xf 0x0" #port 1 + "0x00 0x3a 0x36 0xf 0x0" #port 2 + "0x00 0x38 0x38 0xf 0x0" #port 3 + "0x00 0x37 0x39 0xf 0x0" #port 4 + "0x00 0x2e 0x42 0xf 0x0" #port 5 + "0x00 0x33 0x3d 0xf 0x0" #port 6 + "0x00 0x30 0x40 0xc 0x0" #port 7 + "0x00 0x2f 0x43 0xe 0x0" #port 8 + "0x00 0x2d 0x43 0xc 0x0" #port 9 + "0x00 0x2c 0x44 0xc 0x0" #port 10 + "0x00 0x2d 0x43 0xc 0x0" #port 11 + "0x00 0x29 0x47 0xc 0x0" #port 12 + "0x00 0x28 0x48 0xc 0x0" #port 13 + "0x00 0x29 0x47 0xa 0x0" #port 14 + "0x00 0x29 0x47 0xf 0x0" #port 15 + "0x00 0x18 0x58 0x9 0x0" #port 16 + "0x00 0x1d 0x53 0x9 0x0" #port 17 + "0x00 0x1c 0x54 0x9 0x0" #port 18 + "0x00 0x1c 0x54 0x9 0x0" #port 19 + "0x00 0x1c 0x54 0x9 0x0" #port 20 + "0x00 0x1c 0x54 0x9 0x0" #port 21 + "0x00 0x1c 0x54 0x9 0x0" #port 22 + "0x00 0x1c 0x54 0x9 0x0" #port 23 + "0x00 0x1c 0x54 0x8 0x0" #port 24 + "0x00 0x1c 0x54 0x8 0x0" #port 25 + "0x00 0x18 0x58 0x8 0x0" #port 26 + "0x00 0x18 0x58 0x8 0x0" #port 27 + "0x00 0x14 0x5c 0x8 0x0" #port 28 + "0x00 0x19 0x57 0x8 0x0" #port 29 + "0x00 0x19 0x57 0x8 0x0" #port 30 + "0x00 0x19 0x57 0x8 0x0" #port 31 + "0x00 0x19 0x57 0xc 0x0" #port 32 + "0x00 0x20 0x50 0x9 0x0" #port 33 + "0x00 0x21 0x4f 0xc 0x0" #port 34 + "0x00 0x24 0x4c 0xc 0x0" #port 35 + "0x00 0x24 0x4c 0xc 0x0" #port 36 + "0x00 0x24 0x4c 0xc 0x0" #port 37 + "0x00 0x24 0x4c 0xc 0x0" #port 38 + "0x00 0x24 0x4c 0xc 0x0" #port 39 + "0x00 0x28 0x48 0xd 0x0" #port 40 + "0x00 0x28 0x48 0xc 0x0" #port 41 + "0x00 0x29 0x47 0xe 0x0" #port 42 + "0x00 0x29 0x47 0xe 0x0" #port 43 + "0x00 0x29 0x47 0xe 0x0" #port 44 + "0x00 0x28 0x48 0xf 0x0" #port 45 + "0x00 0x28 0x48 0xf 0x0" #port 46 + "0x00 0x30 0x40 0xf 0x0" #port 47 +) + +ce_dac_si_value=( + "0x00 0x28 0x48 0xc 0x0" #port 0 lane 0 + "0x00 0x29 0x41 0xc 0x0" #port 0 lane 1 + "0x00 0x29 0x41 0xf 0x0" #port 0 lane 2 + "0x00 0x29 0x41 0xc 0x0" #port 0 lane 3 + "0x00 0x28 0x42 0xc 0x0" #port 1 lane 0 + "0x00 0x3a 0x36 0xf 0x0" #port 1 lane 1 + "0x00 0x2c 0x44 0xc 0x0" #port 1 lane 2 + "0x00 0x2c 0x44 0xc 0x0" #port 1 lane 3 + "0x00 0x2f 0x41 0xc 0x0" #port 2 lane 0 + "0x00 0x38 0x38 0xc 0x0" #port 2 lane 1 + "0x00 0x2f 0x41 0xc 0x0" #port 2 lane 2 + "0x00 0x30 0x40 0xc 0x0" #port 2 lane 3 + "0x00 0x30 0x40 0xc 0x0" #port 3 lane 0 + "0x00 0x3f 0x31 0xf 0x0" #port 3 lane 1 + "0x00 0x31 0x3f 0xc 0x0" #port 3 lane 2 + "0x00 0x30 0x40 0xa 0x0" #port 3 lane 3 + "0x00 0x30 0x40 0xc 0x0" #port 4 lane 0 + "0x00 0x31 0x3f 0xf 0x0" #port 4 lane 1 + "0x00 0x2f 0x41 0xc 0x0" #port 4 lane 2 + "0x00 0x2e 0x42 0xc 0x0" #port 4 lane 3 + "0x00 0x30 0x40 0xe 0x0" #port 5 lane 0 + "0x00 0x3f 0x31 0xf 0x0" #port 5 lane 1 + "0x00 0x38 0x38 0xf 0x0" #port 5 lane 2 + "0x00 0x3f 0x31 0xf 0x0" #port 5 lane 3 +) + +#get field value in si value array for xe port +function get_xe_si { + local port=$1 + local field=$2 + local type=$3 + local index=$port + if [ "$type" == "${AOC_TYPE}" ]; then + SI_VAL=$(echo "${xe_aoc_si_value[$index]}" | awk '{print $'$field'}') + else + SI_VAL=$(echo "${xe_dac_si_value[$index]}" | awk '{print $'$field'}') + fi +} + +#get field value in si value array for ce port +function get_ce_si { + local port=$1 + local lane=$2 + local field=$3 + local type=$4 + index=$(( (${port}-1)*4+${lane} )) + if [ "$type" == "${AOC_TYPE}" ]; then + SI_VAL=$(echo "${ce_aoc_si_value[$index]}" | awk '{print $'$field'}') + else + SI_VAL=$(echo "${ce_dac_si_value[$index]}" | awk '{print $'$field'}') + fi +} + +# set si value for xe port +function _qsfp_xe_si_set { + # convert PORT to xe port index + local xe_port=$PORT + # generate command for SI PRE value + get_xe_si $xe_port $SI_PRE_IDX $TYPE + local pre_cmd="phy xe${xe_port} CL93N72_UT_CTL2r CL93N72_TXFIR_PRE=${SI_VAL}" + # generate command for SI POST value + get_xe_si $xe_port $SI_POST_IDX $TYPE + local post_cmd="phy xe${xe_port} CL93N72_UT_CTL2r CL93N72_TXFIR_POST=${SI_VAL}" + # generate command for SI MAIN value + get_xe_si $xe_port $SI_MAIN_IDX $TYPE + local main_cmd="phy xe${xe_port} CL93N72_UT_CTL3r CL93N72_TXFIR_MAIN=${SI_VAL}" + # generate command for SI AMP CONTROL value + get_xe_si $xe_port $SI_AMP_IDX $TYPE + local amp_val=$SI_VAL + get_xe_si $xe_port $SI_DRI_IDX $TYPE + local dri_val=$SI_VAL + local amp_cmd="phy xe${xe_port} AMS_TX_CTL2r AMS_TX_AMP_CTL=${amp_val} AMS_TX_DRIVERMODE=${dri_val}" + # apply bcmcmd + bcmcmd "${pre_cmd};${post_cmd};${main_cmd};${amp_cmd}" +} + +# set aoc si value for ce port +function _qsfp_ce_si_set { + # convert PORT to ce port index + local ce_port=$(( $PORT - 48 )) + for lane in {0..3}; + do + # generate command for SI PRE value + get_ce_si $ce_port $lane $SI_PRE_IDX $TYPE + local pre_cmd="phy ce${ce_port} CL93N72_UT_CTL2r.${lane} CL93N72_TXFIR_PRE=${SI_VAL}" + # generate command for SI POST value + get_ce_si $ce_port $lane $SI_POST_IDX $TYPE + local post_cmd="phy ce${ce_port} CL93N72_UT_CTL2r.${lane} CL93N72_TXFIR_POST=${SI_VAL}" + # generate command for SI MAIN value + get_ce_si $ce_port $lane $SI_MAIN_IDX $TYPE + local main_cmd="phy ce${ce_port} CL93N72_UT_CTL3r.${lane} CL93N72_TXFIR_MAIN=${SI_VAL}" + # generate command for SI AMP CONTROL value + get_ce_si $ce_port $lane $SI_AMP_IDX $TYPE + local amp_val=$SI_VAL + get_ce_si $ce_port $lane $SI_AMP_IDX $TYPE + local dri_val=$SI_VAL + local amp_cmd="phy ce${ce_port} AMS_TX_CTL2r.${lane} AMS_TX_AMP_CTL=${amp_val} AMS_TX_DRIVERMODE=${dri_val}" + # apply bcmcmd + bcmcmd "${pre_cmd};${post_cmd};${main_cmd};${amp_cmd}" + done +} + +#QSFP SI value set +function _qsfp_si_set { + if [[ $PORT -le 47 && $PORT -ge 0 ]]; then + # xe port + _qsfp_xe_si_set + else + # ce port + _qsfp_ce_si_set + fi +} + +function _util_input_check { + # input parameter validation + if [[ $1 -lt $2 || $1 -gt $3 ]]; then + echo "Please input number $2~$3" + exit + fi +} + +# Help usage function +function _help { + echo "=========================================================" + echo "# Description: Help Function" + echo "=========================================================" + echo "----------------------------------------------------" + echo "EX : ${0} help" + echo " : ${0} ${AOC_TYPE} [${MIN_PORT}-${MAX_PORT}]" + echo " : ${0} ${DAC_TYPE} [${MIN_PORT}-${MAX_PORT}]" + echo "----------------------------------------------------" +} + +#Main Function +function _main { + + # TODO: remove after SI value ready + #exit ${TRUE} + + if [ -z $PORT ]; then + _help + exit ${FALSE} + fi + + if [ "${TYPE}" == "help" ]; then + _help + elif [ "${TYPE}" == "${AOC_TYPE}" ] || [ "${TYPE}" == "${DAC_TYPE}" ]; then + _util_input_check "$PORT" "$MIN_PORT" "$MAX_PORT" + _qsfp_si_set + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi +} + +_main diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/utils/s8900_54xc_monitor.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/utils/s8900_54xc_monitor.sh new file mode 100755 index 000000000000..974da6d5001f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-54xc/utils/s8900_54xc_monitor.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=5 +I2C_UTILS="/usr/sbin/i2c_utils.sh" + + +# LED status monitor +function _led_monitor { + ${I2C_UTILS} i2c_led_fan_status_set >/dev/null + ${I2C_UTILS} i2c_led_psu_status_set >/dev/null + ${I2C_UTILS} i2c_led_fan_tray_status_set >/dev/null +} + +# main function +function _main { + while true + do + _led_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/README.md b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/README.md new file mode 100644 index 000000000000..28f395216e3c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/README.md @@ -0,0 +1,185 @@ +# Ingrasys S8900-64XC Platform Driver for SONiC + +Copyright (C) 2016 Ingrasys, Inc. + +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 3 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, see . + + +## Licensing terms + +The Licensing terms of the files within this project is split 2 parts. +The Linux kernel is released under the GNU General Public License version 2. +All other code is released under the GNU General Public License version 3. +Please see the LICENSE file for copies of both licenses. + +## Contents of this package + + - service/ + > Service config files for platform initialization and monitoring + - utils/ + > Scripts useful during platform bringup and sysfs function + - conf/ + > Platform configure files. + +## Kernel modules and drivers + +The driver for interacting with Ingrasys S8900-64XC is contained in the I2C +kernel module and initialization script. The initialization script loads +the modules in the correct order. It has been built and tested against +the Linux 3.16 kernel. + +The initialization script will modprobe the needed modules, navigate to the +module's device directory in sysfs, and write configuration data to +the kernel module. + +### IGB + +This is OOB Ports on front panel for management plane. + +The IGB module must be loaded first on Ingrasys S8900-64XC platform. + +### I2C i801 + +The I2C i801 on Ingrasys S8900-64XC can be found in +`/sys/bus/i2c/devices/i2c-0/` + +This is I2C bus for Clock Gen, DIMM channel and digital potentiometers. + +The i801 module must be loaded second on Ingrasys S8900-64XC. + +### I2C iSMT + +The I2C iSMT module on S8900-64XC can be found in +`/sys/bus/i2c/devices/i2c-1/` + +This is I2C bus for CPLD, HWM, power controller and I2C Switches. + +The i801 module must be loaded third on Ingrasys S8900-64XC. + +### I2C PCA9548 +The PCA9548 module on S8900-64XC can be found in +`/sys/bus/i2c/devices/i2c-2/` , `/sys/bus/i2c/devices/i2c-3/`, +`/sys/bus/i2c/devices/i2c-4/`, `/sys/bus/i2c/devices/i2c-5/`, +`/sys/bus/i2c/devices/i2c-6/`, `/sys/bus/i2c/devices/i2c-7/`, +`/sys/bus/i2c/devices/i2c-8/`, `/sys/bus/i2c/devices/i2c-9/`. + +The pca9548 module for zQSFP module get/set functions, PSU information, +fan status and EEPROM. + +## Hardware components + +The hardware components are initialized in the init script on S8900-64XC. +The following describes manual initialization as well as interaction. +The examples below are just for Ingrasys S8900-64XC platform. + +### Hardware initialization + +When the sonic-platform-ingrasys-s8900-64xc package is installed on the S8900-64XC, +it is automatically initialized. If you want to manual initialization, the +utility command usage as follows: +``` + i2c_utils.sh i2c_init +``` + +### EEPROM + +The EEPROM is including the board SKU, model name, vendor name, serial number, +and other information can be accessed with the specific eeprom kernel module. +After using `modprobe eeprom_mb` to detect the eeprom, it will show up in sysfs. + +The hexdump utility can be used to decode the raw output of the EEPROM. +For example, +``` + bash# echo "mb_eeprom 0x54" > /sys/bus/i2c/devices/i2c-9/new_device + bash# cat /sys/bus/i2c/drivers/mb_eeprom/9-0054/eeprom | hexdump -C +``` + +### Front panel LEDs + +LEDs can be setup on/off by using i2c utility `/usr/sbin/i2c_utils.sh`. +Utility function command usage as follows: + +``` +Status LED: + i2c_utils.sh i2c_sys_led green|amber on|off + +Fan status LED: + i2c_utils.sh i2c_fan_led green|amber on|off + +PSU1 status LED: + i2c_utils.sh i2c_psu1_led green|amber on|off + +PSU2 status LED: + i2c_utils.sh i2c_psu2_led green|amber on|off + +``` +QSFP Module Port LEDs control by ASIC library. + + +### Fan speed + +Fan speed are controlled by the w83795 kernel module. +It can be found in `/sys/class/hwmon/hwmon1/device/`. +If they were compiled as modules, you will need to modprobe w83795 for +their sysfs entries to show up. Each fan has an `fan_input` file +for reading the fan speed. And `pwm1` setting fan1 to fan4, +`pwm2` setting fan5 to fan8. + +There is docker-platform-monitor container installed fancontrol package that can +automatic control platform fan speed. + + +### Temperature sensors + +Temperature sensors are controlled by the w83795 kernel +module. It can be found in `/sys/class/hwmon/hwmon1/device/`. +If they were compiled as modules, then you will need to modprobe w83795 for +their sysfs entries to show up. +`temp1_input` is front MAC temperature sensor. `temp2_input` is rear MAC +temperature sensor. + +There is docker-platform-monitor container installed lm-sensors package that can +monitor platform temperature. And `sensors` command can show each +temperature sensors status. + +### Power supplies + +Power supplies status and its EEPROM info can be used i2c utility +`/usr/sbin/i2c_utils.sh` to get. +The usage as follows: +``` +PSU EEPROM: + i2c_utils.sh i2c_psu_eeprom_get + hexdump -C psu0.rom + hexdump -C psu1.rom + +PSU Status: + i2c_utils.sh i2c_psu_status +``` + +### QSFPs +QSFP modules are managed by the pca9548 kernel driver. +The i2c utility `/usr/sbin/i2c_utils.sh` can be used to get status and +module EEPROM informations. +The usage as follows: +``` +QSFP EEPROM: + i2c_utils.sh i2c_qsfp_eeprom_get [1-32] + +QSFP Insert Event: + i2c_utils.sh i2c_qsfp_eeprom_get [1-32] + 0 => not insert + 1 => inserted +``` + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/Makefile b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/Makefile new file mode 100644 index 000000000000..bf7d4baed71b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/Makefile @@ -0,0 +1,4 @@ +#ccflags-y += -DDEBUG +obj-m := eeprom_mb.o +obj-m+= qsfp_cpld.o +obj-m+= i2c_cpld.o diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/eeprom_mb.c b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/eeprom_mb.c new file mode 100644 index 000000000000..0886cad58eb3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/eeprom_mb.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 1998, 1999 Frodo Looijaard and + * Philip Edelbrock + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003 IBM Corp. + * Copyright (C) 2004 Jean Delvare + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, I2C_CLIENT_END }; + + +/* Size of EEPROM in bytes */ +#define EEPROM_SIZE 256 + +#define SLICE_BITS (6) +#define SLICE_SIZE (1 << SLICE_BITS) +#define SLICE_NUM (EEPROM_SIZE/SLICE_SIZE) + +/* Each client has this additional data */ +struct eeprom_data { + struct mutex update_lock; + u8 valid; /* bitfield, bit!=0 if slice is valid */ + unsigned long last_updated[SLICE_NUM]; /* In jiffies, 8 slices */ + u8 data[EEPROM_SIZE]; /* Register values */ +}; + + +static void mb_eeprom_update_client(struct i2c_client *client, u8 slice) +{ + struct eeprom_data *data = i2c_get_clientdata(client); + int i, j; + int ret; + int addr; + + mutex_lock(&data->update_lock); + + if (!(data->valid & (1 << slice)) || + time_after(jiffies, data->last_updated[slice] + 300 * HZ)) { + dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice); + + addr = slice << SLICE_BITS; + + ret = i2c_smbus_write_byte_data(client, ((u8)addr >> 8) & 0xFF, (u8)addr & 0xFF); + /* select the eeprom address */ + if (ret < 0) { + dev_err(&client->dev, "address set failed\n"); + goto exit; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE)) { + goto exit; + } + + for (i = slice << SLICE_BITS; i < (slice + 1) << SLICE_BITS; i+= SLICE_SIZE) { + for (j = i; j < (i+SLICE_SIZE); j++) { + int res; + + res = i2c_smbus_read_byte(client); + if (res < 0) { + goto exit; + } + + data->data[j] = res & 0xFF; + } + } + + data->last_updated[slice] = jiffies; + data->valid |= (1 << slice); + } +exit: + mutex_unlock(&data->update_lock); +} + +static ssize_t mb_eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct eeprom_data *data = i2c_get_clientdata(client); + u8 slice; + + if (off > EEPROM_SIZE) { + return 0; + } + if (off + count > EEPROM_SIZE) { + count = EEPROM_SIZE - off; + } + if (count == 0) { + return 0; + } + + /* Only refresh slices which contain requested bytes */ + for (slice = off >> SLICE_BITS; slice <= (off + count - 1) >> SLICE_BITS; slice++) { + mb_eeprom_update_client(client, slice); + } + + memcpy(buf, &data->data[off], count); + + return count; +} + +static struct bin_attribute mb_eeprom_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO, + }, + .size = EEPROM_SIZE, + .read = mb_eeprom_read, +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int mb_eeprom_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all + addresses 0x50-0x57, but we only care about 0x50. So decline + attaching to addresses >= 0x51 on DDC buses */ + if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x51) { + return -ENODEV; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE) + && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + return -ENODEV; + } + + strlcpy(info->type, "eeprom", I2C_NAME_SIZE); + + return 0; +} + +static int mb_eeprom_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct eeprom_data *data; + int err; + + if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + memset(data->data, 0xff, EEPROM_SIZE); + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &mb_eeprom_attr); + if (err) { + goto exit_kfree; + } + + return 0; + +exit_kfree: + kfree(data); +exit: + return err; +} + +static int mb_eeprom_remove(struct i2c_client *client) +{ + sysfs_remove_bin_file(&client->dev.kobj, &mb_eeprom_attr); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id mb_eeprom_id[] = { + { "mb_eeprom", 0 }, + { } +}; + +static struct i2c_driver mb_eeprom_driver = { + .driver = { + .name = "mb_eeprom", + }, + .probe = mb_eeprom_probe, + .remove = mb_eeprom_remove, + .id_table = mb_eeprom_id, + + .class = I2C_CLASS_DDC | I2C_CLASS_SPD, + .detect = mb_eeprom_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(mb_eeprom_driver); + +MODULE_AUTHOR("Wade "); +MODULE_DESCRIPTION("Ingrasys S8900 Mother Borad EEPROM driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/i2c_cpld.c b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/i2c_cpld.c new file mode 100644 index 000000000000..626c63177b50 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/i2c_cpld.c @@ -0,0 +1,448 @@ +/* + * S9100-32X I2C CPLD driver + * + * Copyright (C) 2017 Ingrasys, Inc. + * Wade He + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "i2c_cpld.h" + +#ifdef DEBUG + #define DEBUG_PRINT(fmt, args...) \ + printk(KERN_INFO "%s[%d]: " fmt "\r\n", \ + __FUNCTION__, __LINE__, ##args) +#else + #define DEBUG_PRINT(fmt, args...) +#endif + +#define I2C_READ_BYTE_DATA(ret, lock, i2c_client, reg) \ +{ \ + mutex_lock(lock); \ + ret = i2c_smbus_read_byte_data(i2c_client, reg); \ + mutex_unlock(lock); \ +} +#define I2C_WRITE_BYTE_DATA(ret, lock, i2c_client, reg, val) \ +{ \ + mutex_lock(lock); \ + ret = i2c_smbus_write_byte_data(i2c_client, reg, val); \ + mutex_unlock(lock); \ +} + +/* CPLD sysfs attributes index */ +enum i2c_cpld_sysfs_attributes { + CPLD_ACCESS_REG, + CPLD_REGISTER_VAL, + CPLD_PORT_START, + CPLD_PORTS, + CPLD_VERSION, + CPLD_ID, + CPLD_BOARD_TYPE, + CPLD_EXT_BOARD_TYPE, + CPLD_PW_GOOD, + CPLD_PW_ABS, +}; + +/* CPLD sysfs attributes hook functions */ +static ssize_t read_access_register(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t write_access_register(struct device *dev, + struct device_attribute *da, const char *buf, size_t count); +static ssize_t read_register_value(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t write_register_value(struct device *dev, + struct device_attribute *da, const char *buf, size_t count); +static ssize_t read_cpld_version(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_board_type(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_ext_board_type(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_pw_good(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_pw_abs(struct device *dev, + struct device_attribute *da, char *buf); + +static LIST_HEAD(cpld_client_list); /* client list for cpld */ +static struct mutex list_lock; /* mutex for client list */ + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +struct cpld_data { + int index; /* CPLD index */ + struct mutex access_lock; /* mutex for cpld access */ + u8 access_reg; /* register to access */ +}; + +/* CPLD device id and data */ +static const struct i2c_device_id i2c_cpld_id[] = { + { "i2c_cpld", i2c_cpld }, + {} +}; + +/* Addresses scanned for i2c_cpld */ +static const unsigned short cpld_i2c_addr[] = { 0x33, I2C_CLIENT_END }; +/* platform sysfs object */ +extern struct kobject *s9230_64x_kobj; + + +/* define all support register access of cpld in attribute */ +static SENSOR_DEVICE_ATTR(cpld_access_register, S_IWUSR | S_IRUGO, + read_access_register, write_access_register, CPLD_ACCESS_REG); +static SENSOR_DEVICE_ATTR(cpld_register_value, S_IWUSR | S_IRUGO, + read_register_value, write_register_value, CPLD_REGISTER_VAL); +static SENSOR_DEVICE_ATTR(cpld_version, S_IRUGO, + read_cpld_version, NULL, CPLD_VERSION); +static SENSOR_DEVICE_ATTR(cpld_board_type, S_IRUGO, + read_board_type, NULL, CPLD_BOARD_TYPE); +static SENSOR_DEVICE_ATTR(cpld_ext_board_type, S_IRUGO, + read_ext_board_type, NULL, CPLD_EXT_BOARD_TYPE); +static SENSOR_DEVICE_ATTR(cpld_pw_good, S_IRUGO, + read_pw_good, NULL, CPLD_PW_GOOD); +static SENSOR_DEVICE_ATTR(cpld_pw_abs, S_IRUGO, + read_pw_abs, NULL, CPLD_PW_ABS); + + +/* define support attributes of cpldx , total 5 */ +/* cpld 1 */ +static struct attribute *i2c_cpld_attributes[] = { + &sensor_dev_attr_cpld_access_register.dev_attr.attr, + &sensor_dev_attr_cpld_register_value.dev_attr.attr, + &sensor_dev_attr_cpld_version.dev_attr.attr, + &sensor_dev_attr_cpld_board_type.dev_attr.attr, + &sensor_dev_attr_cpld_ext_board_type.dev_attr.attr, + &sensor_dev_attr_cpld_pw_good.dev_attr.attr, + &sensor_dev_attr_cpld_pw_abs.dev_attr.attr, + NULL +}; + +/* cpld 1 attributes group */ +static const struct attribute_group i2c_cpld_group = { + .attrs = i2c_cpld_attributes, +}; + +/* read access register from cpld data */ +static ssize_t read_access_register(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg = data->access_reg; + + return sprintf(buf, "0x%x\n", reg); +} + +/* write access register to cpld data */ +static ssize_t write_access_register(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + + if (kstrtou8(buf, 0, ®) < 0) + return -EINVAL; + + data->access_reg = reg; + return count; +} + +/* read the value of access register in cpld data */ +static ssize_t read_register_value(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg = data->access_reg; + int reg_val; + + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + + if (reg_val < 0) + return -1; + + return sprintf(buf, "0x%x\n", reg_val); +} + +/* wrtie the value to access register in cpld data */ +static ssize_t write_register_value(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + int ret = -EIO; + u8 reg = data->access_reg; + u8 reg_val; + + if (kstrtou8(buf, 0, ®_val) < 0) + return -EINVAL; + + I2C_WRITE_BYTE_DATA(ret, &data->access_lock, client, reg, reg_val); + + return count; +} + +/* get cpdl version register value */ +static ssize_t read_cpld_version(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_VERSION) { + reg = CPLD_VERSION_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get board type register value */ +static ssize_t read_board_type(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_BOARD_TYPE) { + reg = CPLD_BOARD_TYPE_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get extend board type register value */ +static ssize_t read_ext_board_type(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_EXT_BOARD_TYPE) { + reg = CPLD_EXT_BOARD_TYPE_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get extend board type register value */ +static ssize_t read_pw_good(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_PW_GOOD) { + reg = CPLD_PW_GOOD_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get extend board type register value */ +static ssize_t read_pw_abs(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_PW_ABS) { + reg = CPLD_PW_ABS_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* add valid cpld client to list */ +static void i2c_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = NULL; + + node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + if (!node) { + dev_info(&client->dev, + "Can't allocate cpld_client_node for index %d\n", + client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +/* remove exist cpld client in list */ +static void i2c_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + list_for_each(list_node, &cpld_client_list) { + cpld_node = list_entry(list_node, + struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + mutex_unlock(&list_lock); +} + +/* cpld drvier probe */ +static int i2c_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + struct cpld_data *data = NULL; + + data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* init cpld data for client */ + i2c_set_clientdata(client, data); + mutex_init(&data->access_lock); + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_info(&client->dev, + "i2c_check_functionality failed (0x%x)\n", + client->addr); + status = -EIO; + goto exit; + } + + + status = sysfs_create_group(&client->dev.kobj,&i2c_cpld_group); + + if (status) + goto exit; + + dev_info(&client->dev, "chip found\n"); + + /* add probe chip to client list */ + i2c_cpld_add_client(client); + + return 0; +exit: + sysfs_remove_group(&client->dev.kobj, &i2c_cpld_group); + return status; +} + +/* cpld drvier remove */ +static int i2c_cpld_remove(struct i2c_client *client) +{ + + sysfs_remove_group(&client->dev.kobj, &i2c_cpld_group); + + i2c_cpld_remove_client(client); + return 0; +} + +MODULE_DEVICE_TABLE(i2c, i2c_cpld_id); + +static struct i2c_driver i2c_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "i2c_cpld", + }, + .probe = i2c_cpld_probe, + .remove = i2c_cpld_remove, + .id_table = i2c_cpld_id, + .address_list = cpld_i2c_addr, +}; + +static int __init i2c_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&i2c_cpld_driver); +} + +static void __exit i2c_cpld_exit(void) +{ + i2c_del_driver(&i2c_cpld_driver); +} + +MODULE_AUTHOR("Wade He "); +MODULE_DESCRIPTION("Ingrasys S9100-32X Platform i2c cpld driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_cpld_init); +module_exit(i2c_cpld_exit); + + + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/i2c_cpld.h b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/i2c_cpld.h new file mode 100644 index 000000000000..e103b05c717a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/i2c_cpld.h @@ -0,0 +1,197 @@ +/* + * + * S9100-32X I2C CPLD driver header file + * + * Copyright (C) 2017 Ingrasys, Inc. + * Wade He + * + * 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, see . + */ + +#ifndef I2C_CPLD_H +#define I2C_CPLD_H + +// remove debug before release +#define DEBUG + +/* CPLD device index value */ +enum cpld_id { + i2c_cpld +}; + +/* port number on CPLD */ +#define CPLD_1_PORT_NUM 12 +#define CPLD_2_PORT_NUM 13 + +/* QSFP port number */ +#define QSFP_MAX_PORT_NUM 64 +#define QSFP_MIN_PORT_NUM 1 + +/* SFP+ port number */ +#define SFP_MAX_PORT_NUM 2 +#define SFP_MIN_PORT_NUM 1 + + +/* CPLD registers */ +#define CPLD_BOARD_TYPE_REG 0x0 +#define CPLD_EXT_BOARD_TYPE_REG 0x7 +#define CPLD_VERSION_REG 0x1 +#define CPLD_PW_GOOD_REG 0x2 +#define CPLD_PW_ABS_REG 0x3 + + +/* bit definition for register value */ + +enum CPLD_RESET_CONTROL_BITS { + CPLD_RESET_CONTROL_SWRST_BIT, + CPLD_RESET_CONTROL_CP2104RST_BIT, + CPLD_RESET_CONTROL_82P33814RST_BIT, + CPLD_RESET_CONTROL_BMCRST_BIT, +}; + +/* bit field structure for register value */ +struct cpld_reg_board_type_t { + u8 build_rev:2; + u8 hw_rev:2; + u8 board_id:4; +}; + +struct cpld_reg_version_t { + u8 revision:6; + u8 release:1; + u8 reserve:1; +}; + +struct cpld_reg_pw_good_t { + u8 reserve1:3; + u8 psu1:1; + u8 psu2:1; + u8 reserve2:3; +}; + +struct cpld_reg_pw_abs_t { + u8 psu1:1; + u8 psu2:1; + u8 reserve:6; +}; + +/* common manipulation */ +#define INVALID(i, min, max) ((i < min) || (i > max) ? 1u : 0u) +#define READ_BIT(val, bit) ((0u == (val & (1<bf_name) +#define READ_BF_1(bf_struct, val, bf_name, bf_value) \ + bf_struct bf; \ + bf.data = val; \ + bf_value = bf.bf_name +#define BOARD_TYPE_BUILD_REV_GET(val, res) \ + READ_BF(cpld_reg_board_type_t, val, build_rev, res) +#define BOARD_TYPE_HW_REV_GET(val, res) \ + READ_BF(cpld_reg_board_type_t, val, hw_rev, res) +#define BOARD_TYPE_BOARD_ID_GET(val, res) \ + READ_BF(cpld_reg_board_type_t, val, board_id, res) +#define CPLD_VERSION_REV_GET(val, res) \ + READ_BF(cpld_reg_version_t, val, revision, res) +#define CPLD_VERSION_REL_GET(val, res) \ + READ_BF(cpld_reg_version_t, val, release, res) +#define CPLD_PSU1_PW_GOOD_GET(val, res) \ + READ_BF(cpld_reg_pw_good_t, val, psu1, res) +#define CPLD_PSU2_PW_GOOD_GET(val, res) \ + READ_BF(cpld_reg_pw_good_t, val, psu2, res) +#define CPLD_PSU1_PW_ABS_GET(val, res) \ + READ_BF(cpld_reg_pw_abs_t, val, psu1, res) +#define CPLD_PSU2_PW_ABS_GET(val, res) \ + READ_BF(cpld_reg_pw_abs_t, val, psu2, res) +/* QSFP/SFP registers manipulation */ +#define QSFP_TO_CPLD_IDX(qsfp_port, cpld_index, cpld_port) \ +{ \ + if (QSFP_MIN_PORT_NUM <= qsfp_port && qsfp_port <= CPLD_1_PORT_NUM) { \ + cpld_index = cpld1; \ + cpld_port = qsfp_port - 1; \ + } else if (CPLD_1_PORT_NUM < qsfp_port \ + && qsfp_port <= QSFP_MAX_PORT_NUM) { \ + cpld_index = cpld2 + (qsfp_port - 1 - CPLD_1_PORT_NUM) \ + / CPLD_2_PORT_NUM; \ + cpld_port = (qsfp_port - 1 - CPLD_1_PORT_NUM) % \ + CPLD_2_PORT_NUM; \ + } else { \ + cpld_index = 0; \ + cpld_port = 0; \ + } \ +} +#define SFP_TO_CPLD_IDX(sfp_port, cpld_index) \ + (cpld_index = sfp_port - SFP_MIN_PORT_NUM) +#define QSFP_PORT_STATUS_REG(cpld_port) \ + (CPLD_QSFP_PORT_STATUS_BASE_REG + cpld_port) +#define QSFP_PORT_CONFIG_REG(cpld_port) \ + (CPLD_QSFP_PORT_CONFIG_BASE_REG + cpld_port) +#define QSFP_PORT_INT_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_QSFP_PORT_STATUS_INT_BIT) +#define QSFP_PORT_ABS_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_QSFP_PORT_STATUS_ABS_BIT) +#define QSFP_PORT_RESET_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_RESET_BIT) +#define QSFP_PORT_LPMODE_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_LPMODE_BIT) +#define QSFP_PORT_RESET_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_RESET_BIT) +#define QSFP_PORT_RESET_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_RESET_BIT) +#define QSFP_PORT_LPMODE_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_LPMODE_BIT) +#define QSFP_PORT_LPMODE_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_LPMODE_BIT) +#define SFP_PORT_PRESENT_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_STATUS_PRESENT_BIT) + + #define SFP_PORT_TXFAULT_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_STATUS_TXFAULT_BIT) + #define SFP_PORT_RXLOS_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_STATUS_RXLOS_BIT) + #define SFP_PORT_TXDIS_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_CONFIG_TXDIS_BIT) +#define SFP_PORT_RS_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_RS_BIT) +#define SFP_PORT_TS_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TS_BIT) +#define SFP_PORT_TXDIS_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TXDIS_BIT) +#define SFP_PORT_TXDIS_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TXDIS_BIT) +#define SFP_PORT_RS_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_RS_BIT) +#define SFP_PORT_RS_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_RS_BIT) +#define SFP_PORT_TS_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TS_BIT) +#define SFP_PORT_TS_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TS_BIT) + +/* CPLD access functions */ +extern int i2c_cpld_get_qsfp_port_status_val(u8 port_num); +extern int i2c_cpld_get_qsfp_port_config_val(u8 port_num); +extern int i2c_cpld_set_qsfp_port_config_val(u8 port_num, u8 reg_val); +extern int i2c_cpld_get_sfp_port_status_val(u8 port_num); +extern int i2c_cpld_get_sfp_port_config_val(u8 port_num); +extern int i2c_cpld_set_sfp_port_config_val(u8 port_num, u8 reg_val); +extern u8 fp_port_to_phy_port(u8 fp_port); +#endif + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/qsfp_cpld.c b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/qsfp_cpld.c new file mode 100644 index 000000000000..8ec03ecea340 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/modules/qsfp_cpld.c @@ -0,0 +1,1771 @@ +/* + * S8900-64XC QSFP CPLD driver + * + * Copyright (C) 2017 Ingrasys, Inc. + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef DEBUG + #define DEBUG_PRINT(fmt, args...) \ + printk (KERN_INFO "%s[%d]: " fmt "\r\n", \ + __FUNCTION__, __LINE__, ##args) +#else + #define DEBUG_PRINT(fmt, args...) +#endif + +#define ERROR_MSG(fmt, args...) \ + printk(KERN_ERR "%s[%d]: " fmt "\r\n", \ + __FUNCTION__, __LINE__, ##args) + + + + +#define SFF_8436_MMAP_SIZE (256) +#define EEPROM_SIZE (5 * 128) /* 640 byte eeprom */ +#define EEPROM_PAGE_SIZE (128) +#define EEPROM_DEFAULT_PAGE (0) +#define I2C_RW_RETRY_COUNT (3) +#define I2C_RW_RETRY_INTERVAL (100) /* ms */ +#define USE_I2C_BLOCK_READ (1) + +#define SFP_EEPROM_A0_I2C_ADDR (0xA0 >> 1) + +#define SFF_8436_PAGE_PROV_ADDR (0xC3) /* Memory Page 01/02 Provided */ +#define SFF_8436_PAGE_01_PRESENT (1 << 6) /* Memory Page 01 present */ +#define SFF_8436_PAGE_02_PRESENT (1 << 7) /* Memory Page 02 present */ +#define SFF_8436_PAGE_SELECT_ADDR (0x7F) +#define SFF_8436_STATUS_ADDR (0x02) +#define SFF_8436_STATUS_PAGE_03_PRESENT_L (1 << 2) /* Flat Memory:0-Paging, 1-Page 0 only */ + +#define S8900_64XC_MUX_BASE_NR 1 +#define S8900_64XC_SFP_EEPROM_BASE_NR 2 +#define CPLD_DEVICE_NUM 3 +#define SFP_CPLD_DEVICE_NUM 2 +#define QSFP_EEPROM_DEVICE_NUM 3 +#define TOTAL_PORT_NUM 64 +#define CPLD_MUX_OFFSET 24 +#define SFP_EEPROM_DEV_NUM 3 +#define SFP_EEPROM_NAME_LEN 16 +/* CPLD registers */ +#define CPLD_REG_REV 0x01 +#define CPLD_REG_ID 0x02 +#define CPLD_MUX_REG 0x4a + +/* QSFP signal bit in register */ +#define BIT_RST 0 +#define BIT_LPM 2 +#define BIT_INT 0 +#define BIT_ABS 1 +#define BIT_ABS_2 5 + +static ssize_t sfp_eeprom_read(struct i2c_client *, loff_t, u8 *,int); +static ssize_t sfp_eeprom_write(struct i2c_client *, loff_t, const char *,int); + + +enum port_numbers { + sfp1, sfp2, sfp3, sfp4, sfp5, sfp6, sfp7, sfp8, + sfp9, sfp10, sfp11, sfp12, sfp13, sfp14, sfp15, sfp16, + sfp17, sfp18, sfp19, sfp20, sfp21, sfp22, sfp23, sfp24, + sfp25, sfp26, sfp27, sfp28, sfp29, sfp30, sfp31, sfp32, + sfp33, sfp34, sfp35, sfp36, sfp37, sfp38, sfp39, sfp40, + sfp41, sfp42, sfp43, sfp44, sfp45, sfp46, sfp47, sfp48, + qsfp49, qsfp50, qsfp51, qsfp52, qsfp53, qsfp54, qsfp55, qsfp56, + qsfp57, qsfp58, qsfp59, qsfp60, qsfp61, qsfp62, qsfp63, qsfp64 +}; + +static const struct platform_device_id qsfp_device_id[] = { + {"sfp1", sfp1}, {"sfp2", sfp2}, {"sfp3", sfp3}, {"sfp4", sfp4}, + {"sfp5", sfp5}, {"sfp6", sfp6}, {"sfp7", sfp7}, {"sfp8", sfp8}, + {"sfp9", sfp9}, {"sfp10", sfp10}, {"sfp11", sfp11}, {"sfp12", sfp12}, + {"sfp13", sfp13}, {"sfp14", sfp14}, {"sfp15", sfp15}, {"sfp16", sfp16}, + {"sfp17", sfp17}, {"sfp18", sfp18}, {"sfp19", sfp19}, {"sfp20", sfp20}, + {"sfp21", sfp21}, {"sfp22", sfp22}, {"sfp23", sfp23}, {"sfp24", sfp24}, + {"sfp25", sfp25}, {"sfp26", sfp26}, {"sfp27", sfp27}, {"sfp28", sfp28}, + {"sfp29", sfp29}, {"sfp30", sfp30}, {"sfp31", sfp31}, {"sfp32", sfp32}, + {"sfp33", sfp33}, {"sfp34", sfp34}, {"sfp35", sfp35}, {"sfp36", sfp36}, + {"sfp37", sfp37}, {"sfp38", sfp38}, {"sfp39", sfp39}, {"sfp40", sfp40}, + {"sfp41", sfp41}, {"sfp42", sfp42}, {"sfp43", sfp43}, {"sfp44", sfp44}, + {"sfp45", sfp45}, {"sfp46", sfp46}, {"sfp47", sfp47}, {"sfp48", sfp48}, + {"qsfp49", qsfp49}, {"qsfp50", qsfp50}, {"qsfp51", qsfp51}, {"qsfp52", qsfp52}, + {"qsfp53", qsfp53}, {"qsfp54", qsfp54}, {"qsfp55", qsfp55}, {"qsfp56", qsfp56}, + {"qsfp57", qsfp57}, {"qsfp58", qsfp58}, {"qsfp59", qsfp59}, {"qsfp60", qsfp60}, + {"qsfp61", qsfp61}, {"qsfp62", qsfp62}, {"qsfp63", qsfp63}, {"qsfp64", qsfp64}, + {} +}; +MODULE_DEVICE_TABLE(platform, qsfp_device_id); + +/* + * list of valid port types + * note OOM_PORT_TYPE_NOT_PRESENT to indicate no + * module is present in this port + */ +typedef enum oom_driver_port_type_e { + OOM_DRIVER_PORT_TYPE_INVALID, + OOM_DRIVER_PORT_TYPE_NOT_PRESENT, + OOM_DRIVER_PORT_TYPE_SFP, + OOM_DRIVER_PORT_TYPE_SFP_PLUS, + OOM_DRIVER_PORT_TYPE_QSFP, + OOM_DRIVER_PORT_TYPE_QSFP_PLUS, + OOM_DRIVER_PORT_TYPE_QSFP28 +} oom_driver_port_type_t; + +enum driver_type_e { + DRIVER_TYPE_SFP_MSA, + DRIVER_TYPE_SFP_DDM, + DRIVER_TYPE_QSFP +}; + +typedef enum eeprom_operation_e { + EEPROM_READ, + EEPROM_WRITE +} eeprom_operation_t; + +/* + * Each client has this additional data + */ +struct eeprom_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + struct bin_attribute bin; /* eeprom data */ +}; + +struct qsfp_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 status[3]; /* bit0:port0, bit1:port1 and so on */ + /* index 0 => tx_fail + 1 => tx_disable + 2 => rx_loss + */ + u8 device_id; + struct eeprom_data eeprom; +}; + +struct sfp_port_data { + struct mutex update_lock; + enum driver_type_e driver_type; + int port; /* CPLD port index */ + oom_driver_port_type_t port_type; + u64 present; /* present status, bit0:port0, bit1:port1 and so on */ + + struct qsfp_data *qsfp; + + struct i2c_client *client; +}; + +enum sfp_sysfs_attributes { + PRESENT, + PRESENT_ALL, + PORT_NUMBER, + PORT_TYPE, + DDM_IMPLEMENTED, + TX_FAULT, + TX_FAULT1, + TX_FAULT2, + TX_FAULT3, + TX_FAULT4, + TX_DISABLE, + TX_DISABLE1, + TX_DISABLE2, + TX_DISABLE3, + TX_DISABLE4, + TX_DISABLE_ALL, + RX_LOS, + RX_LOS1, + RX_LOS2, + RX_LOS3, + RX_LOS4, + RX_LOS_ALL, +}; + +static void device_release(struct device *dev) +{ + return; +} + +/* + * S8900-64XC CPLD register addresses + */ +static const int int_abs_reg[CPLD_DEVICE_NUM][2]= { + {0x20, 0x31}, + {0x20, 0x31}, + {0x20, 0x2F}, +}; + +static const int rst_lp_reg[CPLD_DEVICE_NUM][2]= { + {0xFF, 0xFF}, /*dummy*/ + {0xFF, 0xFF}, /*dummy*/ + {0x30, 0x3F}, +}; + +/* + * S8900-64XC CPLD + */ + +enum cpld_type { + cpld_1, + cpld_2, + cpld_3, +}; + +enum qsfp_signal { + sig_int, + sig_abs, + sig_rst, + sig_lpm +}; + +struct cpld_platform_data { + int reg_addr; + struct i2c_client *client; +}; + +struct sfp_platform_data { + int reg_addr; + int parent; + int cpld_reg; + struct i2c_client *client; +}; + +static struct cpld_platform_data s8900_64xc_cpld_platform_data[] = { + [cpld_1] = { + .reg_addr = 0x33, + }, + + [cpld_2] = { + .reg_addr = 0x33, + }, + + [cpld_3] = { + .reg_addr = 0x33, + } +}; + +static struct sfp_platform_data s8900_64xc_sfp_platform_data[] = { + [sfp1] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x01, + }, + + [sfp2] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x02, + }, + + [sfp3] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x03, + }, + [sfp4] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x04, + }, + + [sfp5] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x05, + }, + + [sfp6] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x06, + }, + [sfp7] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x07, + }, + + [sfp8] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x08, + }, + + [sfp9] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x09, + }, + [sfp10] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x0A, + }, + + [sfp11] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x0B, + }, + + [sfp12] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x0C, + }, + [sfp13] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x0D, + }, + + [sfp14] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x0E, + }, + + [sfp15] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x0F, + }, + [sfp16] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x10, + }, + + [sfp17] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x11, + }, + + [sfp18] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x12, + }, + [sfp19] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x13, + }, + + [sfp20] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x14, + }, + + [sfp21] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x15, + }, + [sfp22] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x16, + }, + + [sfp23] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x17, + }, + + [sfp24] = { + .reg_addr = 0x50, + .parent = 2, + .cpld_reg = 0x18, + }, + [sfp25] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x01, + }, + + [sfp26] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x02, + }, + [sfp27] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x03, + }, + + [sfp28] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x04, + }, + + [sfp29] = { + .reg_addr = 0x50, + + .parent = 3, + .cpld_reg = 0x05, + }, + [sfp30] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x06, + }, + [sfp31] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x07, + }, + [sfp32] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x08, + }, + [sfp33] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x09, + }, + [sfp34] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x0A, + }, + [sfp35] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x0B, + }, + [sfp36] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x0C, + }, + [sfp37] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x0D, + }, + [sfp38] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x0E, + }, + [sfp39] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x0F, + }, + [sfp40] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x10, + }, + [sfp41] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x11, + }, + [sfp42] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x12, + }, + [sfp43] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x13, + }, + [sfp44] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x14, + }, + [sfp45] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x15, + }, + [sfp46] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x16, + }, + [sfp47] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x17, + }, + [sfp48] = { + .reg_addr = 0x50, + .parent = 3, + .cpld_reg = 0x18, + }, + [qsfp49] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x01, + }, + [qsfp50] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x02, + }, + [qsfp51] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x03, + }, + [qsfp52] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x04, + }, + [qsfp53] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x05, + }, + [qsfp54] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x06, + }, + [qsfp55] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x07, + }, + [qsfp56] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x08, + }, + [qsfp57] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x09, + }, + [qsfp58] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x0A, + }, + [qsfp59] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x0B, + }, + [qsfp60] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x0C, + }, + [qsfp61] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x0D, + }, + [qsfp62] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x0E, + }, + [qsfp63] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x0F, + }, + [qsfp64] = { + .reg_addr = 0x50, + .parent = 4, + .cpld_reg = 0x10, + } +}; + +static struct platform_device s8900_64xc_cpld = { + .name = "ingrasys-s8900-64xc-cpld", + .id = 0, + .dev = { + .platform_data = s8900_64xc_cpld_platform_data, + .release = device_release + }, +}; + +static struct platform_device s8900_64xc_sfp = { + .name = "ingrasys-s8900-64xc-sfp", + .id = 0, + .dev = { + .platform_data = s8900_64xc_sfp_platform_data, + .release = device_release + }, +}; + +/* + * S8900-64XC I2C DEVICES + */ + +struct i2c_device_platform_data { + int parent; + struct i2c_board_info info; + struct i2c_client *client; +}; + +/* module_platform_driver */ +static ssize_t +get_prs_cpld_reg(struct device *dev, + struct device_attribute *devattr, + char *buf, int signal) +{ + int ret; + u64 data = 0; + u64 shift = 0; + int i = 0; + int j = 0; + int port = 0; + int bit = 0; + int bit_mask = 0; + int bit_mask_2 = 0; + int (*reg)[CPLD_DEVICE_NUM][2]; + struct cpld_platform_data *pdata = NULL; + + pdata = dev->platform_data; + + switch(signal) { + case sig_int: + bit = BIT_INT; + reg = (typeof(reg)) &int_abs_reg; + break; + case sig_abs: + bit = BIT_ABS; + reg = (typeof(reg)) &int_abs_reg; + break; + default: + return sprintf(buf, "signal/na"); + } + bit_mask = 0x1 << bit; + bit_mask_2 = 0x1 << BIT_ABS_2; + + for (i=0; i= 0x2a && j <= 0x2f && i < SFP_CPLD_DEVICE_NUM) { + continue; + } + ret = i2c_smbus_read_byte_data(pdata[i].client, j); + if (ret < 0) { + return sprintf((char *)buf, "i2c_smbus_read_byte_data/na"); + } + shift = ((u64) ((ret & bit_mask) >> bit)) << port; + data |= shift; + DEBUG_PRINT("port=%d, shift=0x%016llx, ret=%x, bit_mask=%08x, bit=%08x, data=0x%016llx\n", port, shift, ret, bit_mask, bit, data); + /* CPLD2 and CPLD3 have BIT 1 and BIT 5 for present */ + if (i < SFP_CPLD_DEVICE_NUM) { + port++; + shift = ((u64) ((ret & bit_mask_2) >> BIT_ABS_2)) << port; + data |= shift; + DEBUG_PRINT("port=%d, shift=0x%016llx, ret=%x, bit_mask_2=0x%x, bit=%x, data=0x%016llx\n", port, shift, ret, bit_mask_2, bit, data); + } + + port++; + } + } + + return sprintf((char *)buf, "0x%016llx\n", data); +} + +/* module_platform_driver */ +static ssize_t +get_rst_lp_cpld_reg(struct device *dev, + struct device_attribute *devattr, + char *buf, int signal) +{ + int ret; + u64 data = 0; + u64 shift = 0; + int i = 0; + int j = 0; + int port = 0; + int bit = 0; + int bit_mask = 0; + int (*reg)[CPLD_DEVICE_NUM][2]; + struct cpld_platform_data *pdata = NULL; + + pdata = dev->platform_data; + + switch(signal) { + case sig_rst: + bit = BIT_RST; + reg = (typeof(reg)) &rst_lp_reg; + break; + case sig_lpm: + bit = BIT_LPM; + reg = (typeof(reg)) &rst_lp_reg; + break; + default: + return sprintf(buf, "na"); + } + bit_mask = 0x1 << bit; + + for (i=2; i> bit)) << port; + data |= shift; + port++; + } + } + + return sprintf(buf, "0x%04llx\n", data); +} + +static ssize_t +set_rst_lp_cpld_reg(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count, int signal) +{ + unsigned long data; + int err; + struct cpld_platform_data *pdata = dev->platform_data; + u8 current_reg_val = 0; + u8 new_reg_val = 0; + int value; + int i = 0; + int j = 0; + int port = 0; + int ret = 0; + int bit = 0; + int (*reg)[CPLD_DEVICE_NUM][2]; + + err = kstrtoul(buf, 16, &data); + if (err) + return err; + + switch(signal) { + case sig_rst: + bit = BIT_RST; + reg = (typeof(reg)) &rst_lp_reg; + break; + case sig_lpm: + bit = BIT_LPM; + reg = (typeof(reg)) &rst_lp_reg; + break; + default: + return sprintf((char *)buf, "signal/na"); + } + + for (i=2; i> port) & 0x1; + + //set value on bit N of new_reg_val + if (value > 0) { + new_reg_val = current_reg_val | (u8) (0x1 << bit); + } else { + new_reg_val = current_reg_val & (u8) ~(0x1 << bit); + } + //write reg value if changed + if (current_reg_val != new_reg_val) { + ret = i2c_smbus_write_byte_data(pdata[i].client, j, + (u8)(new_reg_val)); + if (ret < 0){ + return ret; + } + } + port++; + } + } + + return count; +} + +static ssize_t +get_lpmode(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return get_rst_lp_cpld_reg(dev, devattr, buf, sig_lpm); +} + +static ssize_t +set_lpmode(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + return set_rst_lp_cpld_reg(dev, devattr, buf, count, sig_lpm); +} + +static ssize_t +get_reset(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return get_rst_lp_cpld_reg(dev, devattr, buf, sig_rst); +} + +static ssize_t +set_reset(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + return set_rst_lp_cpld_reg(dev, devattr, buf, count, sig_rst); +} + +static ssize_t +get_modprs(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return get_prs_cpld_reg(dev, devattr, buf, sig_abs); +} + +static DEVICE_ATTR(qsfp_modprs, S_IRUGO, get_modprs, NULL); +static DEVICE_ATTR(qsfp_lpmode, S_IRUGO | S_IWUSR, get_lpmode, set_lpmode); +static DEVICE_ATTR(qsfp_reset, S_IRUGO | S_IWUSR, get_reset, set_reset); + +static struct attribute *s8900_64xc_cpld_attrs[] = { + &dev_attr_qsfp_lpmode.attr, + &dev_attr_qsfp_reset.attr, + &dev_attr_qsfp_modprs.attr, + NULL, +}; + +static struct attribute_group s8900_64xc_cpld_attr_grp = { + .attrs = s8900_64xc_cpld_attrs, +}; + +/* + * Assumes that sanity checks for offset happened at sysfs-layer. + * Offset within Lower Page 00h and Upper Page 00h are not recomputed + */ +static uint8_t +sff_8436_translate_offset(loff_t *offset) +{ + unsigned page = 0; + + if (*offset < SFF_8436_MMAP_SIZE) { + return 0; + } + + page = (*offset >> 7)-1; + + if (page > 0 ) { + *offset = 0x80 + (*offset & 0x7f); + } else { + *offset &= 0xff; + } + + return page; +} + +static ssize_t +eeprom_read(struct i2c_client *client, + u8 command, const char *data, + int data_len) +{ +#if USE_I2C_BLOCK_READ + int result, retry = I2C_RW_RETRY_COUNT; + + if (data_len > I2C_SMBUS_BLOCK_MAX) { + data_len = I2C_SMBUS_BLOCK_MAX; + } + + while (retry) { + result = i2c_smbus_read_i2c_block_data(client, command, + data_len, (u8 *)data); + if (result < 0) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(result < 0)) { + goto abort; + } + + if (unlikely(result != data_len)) { + result = -EIO; + goto abort; + } + +abort: + return result; +#else + int result, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + result = i2c_smbus_read_byte_data(client, command); + if (result < 0) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(result < 0)) { + dev_dbg(&client->dev, "sfp read byte data failed, " \\ + "command(0x%2x), data(0x%2x)\r\n", + command, result); + goto abort; + } + + *data = (u8)result; + result = 1; + +abort: + return result; +#endif +} + +static ssize_t +eeprom_write(struct i2c_client *client, + u8 command, const char *data, + int data_len) +{ +#if USE_I2C_BLOCK_READ + int result, retry = I2C_RW_RETRY_COUNT; + + if (data_len > I2C_SMBUS_BLOCK_MAX) { + data_len = I2C_SMBUS_BLOCK_MAX; + } + + while (retry) { + result = i2c_smbus_write_i2c_block_data(client, command, data_len, data); + if (result < 0) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(result < 0)) { + return result; + } + + return data_len; +#else + int result, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + result = i2c_smbus_write_byte_data(client, command, *data); + if (result < 0) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(result < 0)) { + return result; + } + + return 1; +#endif +} + +static ssize_t +sfp_eeprom_read_write(struct i2c_client *client, + eeprom_operation_t op, + loff_t off, + const char *data, + int data_len) +{ + u8 page, phy_page; + u8 val, refresh_page = 0; + int ret; + ssize_t retval = 0; + size_t pending_len = 0, page_len = 0; + loff_t page_offset = 0, page_start_offset = 0; + loff_t phy_offset; + + + if (off > EEPROM_SIZE) { + return 0; + } + + if (off + data_len > EEPROM_SIZE) { + data_len = EEPROM_SIZE - off; + } + + /* + * Refresh pages which covers the requested data + * from offset to off + len + * Only refresh pages which contain requested bytes + * + */ + + pending_len = data_len; + + for (page = off >> 7; page <= (off + data_len - 1) >> 7; page++) { + refresh_page = 0; + switch (page) { + case 0: + /* Lower page 00h */ + refresh_page = 1; + break; + case 1: + /* Upper page 00h */ + refresh_page = 1; + break; + case 2: + /* Upper page 01h */ + ret = eeprom_read(client, SFF_8436_PAGE_PROV_ADDR, &val, sizeof(val)); + if (ret < 0) { + DEBUG_PRINT("Can't read EEPROM offset %d.\n", + SFF_8436_PAGE_PROV_ADDR); + goto error; + } + if (val & SFF_8436_PAGE_01_PRESENT) { + DEBUG_PRINT("Offset:%d Value:(0x%02x & 0x%02x)", + SFF_8436_PAGE_PROV_ADDR, val, + SFF_8436_PAGE_01_PRESENT); + refresh_page = 1; + } + break; + case 3: + /* Upper page 02h */ + ret = eeprom_read(client, SFF_8436_PAGE_PROV_ADDR, &val, sizeof(val)); + if (ret < 0) { + ERROR_MSG("Can't read EEPROM offset %d.\n", + SFF_8436_PAGE_PROV_ADDR); + goto error; + } + if (val & SFF_8436_PAGE_02_PRESENT) { + DEBUG_PRINT("Offset:%d Value:(0x%02x & 0x%02x)", + SFF_8436_PAGE_PROV_ADDR, val, + SFF_8436_PAGE_02_PRESENT); + refresh_page = 1; + } + break; + case 4: + /* Upper page 03h */ + ret = eeprom_read(client, SFF_8436_STATUS_ADDR, &val, sizeof(val)); + if (ret < 0) { + ERROR_MSG("Can't read EEPROM offset %d.\n", + SFF_8436_STATUS_ADDR); + goto error; + } + if (!(val & SFF_8436_STATUS_PAGE_03_PRESENT_L)) { + DEBUG_PRINT("Offset:%d Value:(0x%02x & 0x%02x)", + SFF_8436_STATUS_ADDR, val, + SFF_8436_STATUS_PAGE_03_PRESENT_L); + refresh_page = 1; + } + break; + default: + DEBUG_PRINT("Invalid Page %d\n", page); + ret = retval; + goto error; + break; + } + + if (!refresh_page) { + /* if page is not valid or already refreshed */ + continue; + } + + /* + * Compute the offset and number of bytes to be read/write + * w.r.t requested page + * + * 1. start at offset 0 (within the page), and read/write the entire page + * 2. start at offset 0 (within the page) and read/write less than entire page + * 3. start at an offset not equal to 0 and read/write the rest of the page + * 4. start at an offset not equal to 0 and read/write less than (end of page - offset) + * + */ + page_start_offset = page * EEPROM_PAGE_SIZE; + + if (page_start_offset < off) { + page_offset = off; + if (off + pending_len < page_start_offset + EEPROM_PAGE_SIZE) { + page_len = pending_len; + } else { + page_len = EEPROM_PAGE_SIZE - off; + } + } else { + page_offset = page_start_offset; + if (pending_len > EEPROM_PAGE_SIZE) { + page_len = EEPROM_PAGE_SIZE; + } else { + page_len = pending_len; + } + } + + pending_len = pending_len - page_len; + + /* Change current EEPROM page */ + phy_offset = page_offset; + phy_page = sff_8436_translate_offset(&phy_offset); + if (phy_page > 0) { + ret = eeprom_write(client, SFF_8436_PAGE_SELECT_ADDR, + &phy_page, sizeof(phy_page)); + if (ret < 0) { + ERROR_MSG("Can't write EEPROM offset %d.\n", + SFF_8436_PAGE_SELECT_ADDR); + goto error; + } + } + + /* + * If page_len > 32, I2C client continue read or write EEPROM. + */ + while (page_len) { + if (op == EEPROM_READ) { + ret = eeprom_read(client, phy_offset, data, page_len); + } else if (op == EEPROM_WRITE) { + ret = eeprom_write(client, phy_offset, data, page_len); + } else { + ERROR_MSG("Bad EEPROM operation %d.\n", op); + break; + } + + if (ret <= 0) { + if (retval == 0) { + retval = ret; + } + break; + } + phy_offset += ret; + off += ret; + data += ret; + page_len -= ret; + retval += ret; + } + + /* Restore EEPROM page to default */ + if (phy_page > 0) { + phy_page = EEPROM_DEFAULT_PAGE; + ret = eeprom_write(client, SFF_8436_PAGE_SELECT_ADDR, + &phy_page, sizeof(phy_page)); + if (ret < 0) { + ERROR_MSG("Can't write EEPROM offset %d.\n", + SFF_8436_PAGE_SELECT_ADDR); + goto error; + } + } + } + + return retval; + +error: + return ret; +} + + +static inline ssize_t +sfp_eeprom_read(struct i2c_client *client, + loff_t off, u8 *data, + int data_len) +{ + return sfp_eeprom_read_write(client, EEPROM_READ, + off, data, data_len); +} + + +static inline ssize_t +sfp_eeprom_write(struct i2c_client *client, + loff_t off, const char *data, + int data_len) +{ + return sfp_eeprom_read_write(client, EEPROM_WRITE, + off, data, data_len); +} + +static struct i2c_client * +cpld_sfp_port_client(int port, + int *data_reg) +{ + *data_reg = s8900_64xc_sfp_platform_data[port].cpld_reg; + if (port >= sfp1 && port <= sfp24) { + return s8900_64xc_cpld_platform_data[cpld_1].client; + } else if (port >= sfp25 && port <= sfp48) { + return s8900_64xc_cpld_platform_data[cpld_2].client; + } else if (port >= qsfp49 && port <= qsfp64) { + return s8900_64xc_cpld_platform_data[cpld_3].client; + } else { + ERROR_MSG("Unknown port: %d", port); + return NULL; + } +} + +static ssize_t +sfp_port_read(struct sfp_port_data *data, + char *buf, loff_t off, + size_t count, int port) +{ + ssize_t retval = 0; + int data_reg = 0; + struct i2c_client *cpld_client = NULL; + + if (unlikely(!count)) { + DEBUG_PRINT("Count = 0, return"); + return count; + } + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&data->update_lock); + + //CPLD MUX select + cpld_client = cpld_sfp_port_client(port, &data_reg); + DEBUG_PRINT("data_reg=%d, port=%d", data_reg, port); + if (!cpld_client) { + ERROR_MSG("Error i2c client for port %d", port); + return 0; + } + i2c_smbus_write_byte_data(cpld_client, CPLD_MUX_REG ,data_reg); + + while (count) { + ssize_t status; + + status = sfp_eeprom_read(data->client, off, buf, count); + if (status <= 0) { + if (retval == 0) { + retval = status; + } + break; + } + + buf += status; + off += status; + count -= status; + retval += status; + } + + //CPLD MUX deselect + i2c_smbus_write_byte_data(cpld_client, CPLD_MUX_REG ,0x0); + + mutex_unlock(&data->update_lock); + return retval; +} + +static ssize_t +sfp_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct sfp_port_data *data; + struct platform_device_id *dev_id = NULL; + + dev_id = (struct platform_device_id *)attr->private; + + DEBUG_PRINT("offset = (%lld), count = (%ld) dev_port=%d", off, count, (int)dev_id->driver_data); + data = dev_get_drvdata(container_of(kobj, struct device, kobj)); + + return sfp_port_read(data, buf, off, count, (int)dev_id->driver_data); +} + +static ssize_t +sfp_port_write(struct sfp_port_data *data, + const char *buf, loff_t off, + size_t count, int port) +{ + ssize_t retval = 0; + int data_reg = 0; + struct i2c_client *cpld_client = NULL; + + if (unlikely(!count)) { + return count; + } + + /* + * Write data to chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&data->update_lock); + + //CPLD MUX select + cpld_client = cpld_sfp_port_client(port, &data_reg); + DEBUG_PRINT("data_reg=%d, port=%d", data_reg, port); + if (!cpld_client) { + ERROR_MSG("Error i2c client for port %d", port); + } + i2c_smbus_write_byte_data(cpld_client, CPLD_MUX_REG ,data_reg); + + while (count) { + ssize_t status; + + status = sfp_eeprom_write(data->client, off, buf, count); + if (status <= 0) { + if (retval == 0) { + retval = status; + } + break; + } + buf += status; + off += status; + count -= status; + retval += status; + } + + //CPLD MUX deselect + i2c_smbus_write_byte_data(cpld_client, CPLD_MUX_REG ,0x0); + + mutex_unlock(&data->update_lock); + return retval; +} + + +static ssize_t +sfp_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct sfp_port_data *data; + struct platform_device_id *dev_id = NULL; + + dev_id = (struct platform_device_id *)attr->private; + + DEBUG_PRINT("offset = (%lld), count = (%ld) dev_port=%d", + off, count, (int)dev_id->driver_data); + data = dev_get_drvdata(container_of(kobj, struct device, kobj)); + + return sfp_port_write(data, buf, off, count, (int)dev_id->driver_data); +} + +static int +sfp_sysfs_eeprom_init(struct kobject *kobj, + struct bin_attribute *eeprom, + const struct platform_device_id *dev_id) +{ + int err; + + sysfs_bin_attr_init(eeprom); + eeprom->attr.name = dev_id->name; + eeprom->attr.mode = S_IWUSR | S_IRUGO; + eeprom->read = sfp_bin_read; + eeprom->write = sfp_bin_write; + eeprom->private = (void *)dev_id; + eeprom->size = EEPROM_SIZE; + + /* Create eeprom file */ + err = sysfs_create_bin_file(kobj, eeprom); + if (err) { + DEBUG_PRINT("err=%d", err); + return err; + } + + return 0; +} + +static int +sfp_sysfs_eeprom_cleanup(struct kobject *kobj, + struct bin_attribute *eeprom) +{ + sysfs_remove_bin_file(kobj, eeprom); + + return 0; +} + +static int +sfp_i2c_check_functionality(struct i2c_client *client) +{ +#if USE_I2C_BLOCK_READ + return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK); +#else + return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA); +#endif +} + +static int +qsfp_probe(struct i2c_client *client, + const struct platform_device_id *dev_id, + struct qsfp_data **data) +{ + int status; + struct qsfp_data *qsfp; + + if (!sfp_i2c_check_functionality(client)) { + status = -EIO; + goto exit; + } + + qsfp = kzalloc(sizeof(struct qsfp_data), GFP_KERNEL); + if (!qsfp) { + status = -ENOMEM; + DEBUG_PRINT("No memory."); + goto exit; + } + + /* Register sysfs hooks */ //TBD: must remove + /* status = sysfs_create_group(&client->dev.kobj, &qsfp_group); + if (status) { + goto exit_free; + } */ + + /* init eeprom */ + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &qsfp->eeprom.bin, dev_id); + if (status) { + DEBUG_PRINT("sfp_sysfs_eeprom_init error"); + goto exit_free; + } + + //TBD: Must remove + /*if (s9130_32x_kobj) { + status = sysfs_create_link(s9130_32x_kobj, &client->dev.kobj, client->name); + if (status) { + goto exit_remove; + } + }*/ + + *data = qsfp; + dev_info(&client->dev, "qsfp '%s'\n", client->name); + + return 0; + +exit_free: + kfree(qsfp); +exit: + + return status; +} + +static int +qsfp_device_probe(struct platform_device *pdev) +{ + struct sfp_platform_data *pdata; + struct sfp_port_data *data[TOTAL_PORT_NUM]; + struct i2c_adapter *parent[SFP_EEPROM_DEV_NUM]; + struct i2c_client *sfp_client[SFP_EEPROM_DEV_NUM]; + int i; + int ret=0; + + DEBUG_PRINT("Start"); + + pdata = pdev->dev.platform_data; + if (!pdata) { + ERROR_MSG("Missing platform data\n"); + return -ENODEV; + } + + //New eeprom device + for (i=0; i < SFP_EEPROM_DEV_NUM; i++) { + parent[i] = i2c_get_adapter(i+S8900_64XC_SFP_EEPROM_BASE_NR); + if (!parent[i]) { + ERROR_MSG("Parent adapter (%d) not found\n", i+S8900_64XC_SFP_EEPROM_BASE_NR); + ret=-ENODEV; + goto error; + } + sfp_client[i] = i2c_new_dummy(parent[i], SFP_EEPROM_A0_I2C_ADDR); + if (!sfp_client[i]) { + ERROR_MSG("[%d]: Fail to create dummy i2c client for parent %d addr 0x%x\n", i, i+S8900_64XC_SFP_EEPROM_BASE_NR, SFP_EEPROM_A0_I2C_ADDR); + ret=-ENODEV; + goto error; + } + } + + //Assign client to dummy device + for (i = 0; i < TOTAL_PORT_NUM; i++) { + switch (pdata[i].parent) { + case 2: + pdata[i].client = sfp_client[0]; + break; + case 3: + pdata[i].client = sfp_client[1]; + break; + case 4: + pdata[i].client = sfp_client[2]; + break; + default: + ERROR_MSG("Error parent number: %d, ", i); + break; + } + + if (!pdata[i].client) { + ERROR_MSG("[%d]: Fail to create dummy i2c client for parent %d addr 0x%x\n", i, pdata[i].parent, pdata[i].reg_addr); + ret=-ENODEV; + goto error; + } + } + + + for (i = 0; i < TOTAL_PORT_NUM; i++) { + data[i] = kzalloc(sizeof(struct sfp_port_data), GFP_KERNEL); + if (!data[i]) { + ret=-ENOMEM; + ERROR_MSG("No memory"); + goto error; + } + + i2c_set_clientdata(pdata[i].client, data[i]); + mutex_init(&data[i]->update_lock); + data[i]->port = qsfp_device_id[i].driver_data; + data[i]->client = pdata[i].client; + + DEBUG_PRINT("data[%d]->port=%d", i, data[i]->port); + + if (pdata[i].client->addr != SFP_EEPROM_A0_I2C_ADDR) { + ret=-ENODEV; + ERROR_MSG("Not approve device address"); + goto error; + } + + data[i]->driver_type = DRIVER_TYPE_QSFP; + + ret |= qsfp_probe(pdata[i].client, &qsfp_device_id[i], &data[i]->qsfp); + } + + if (ret) { + ERROR_MSG("qsfp_probe failed someone."); + //goto error; + } + return 0; +error: + DEBUG_PRINT("error start"); + i2c_put_adapter(parent[i]); + i--; + for (; i >= 0; i--) { + if (pdata[i].client) { + i2c_unregister_device(pdata[i].client); + i2c_put_adapter(parent[i]); + } + } + + return ret; +} + +static int +qsfp_remove(struct i2c_client *client, + struct qsfp_data *data) +{ + //TBD: Must remove + /*if (s9130_32x_kobj) { + sysfs_remove_link(s9130_32x_kobj, client->name); + }*/ + + //TBD: Must remove all ports EEPROM BIN + sfp_sysfs_eeprom_cleanup(&client->dev.kobj, &data->eeprom.bin); + //TBD: Must remove sysfs_remove_group(&client->dev.kobj, &qsfp_group); + kfree(data); + + return 0; +} + +static int __exit +qsfp_device_remove(struct platform_device *pdev) +{ + struct sfp_port_data *data = NULL; + struct sfp_platform_data *pdata = pdev->dev.platform_data; + struct i2c_adapter *parent = NULL; + int i; + + + + if (!pdata) { + ERROR_MSG("Missing platform data\n"); + return -ENOENT; + } + + for (i = 0; i < TOTAL_PORT_NUM; i+=CPLD_MUX_OFFSET) { + data = i2c_get_clientdata(pdata[i].client); + if (!data) { + ERROR_MSG("Empty data. skip. i=%d", i); + continue; + } + qsfp_remove(pdata[i].client, data->qsfp); + if (pdata[i].client) { + parent = (pdata[i].client)->adapter; + i2c_unregister_device(pdata[i].client); + i2c_put_adapter(parent); + } + kfree(data); + } + + + return 0; +} + +static int +cpld_probe(struct platform_device *pdev) +{ + struct cpld_platform_data *pdata; + struct i2c_adapter *parent[CPLD_DEVICE_NUM]; + int i; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + ERROR_MSG("Missing platform data\n"); + return -ENODEV; + } + + for (i = 0; i < CPLD_DEVICE_NUM; i++) { + parent[i] = i2c_get_adapter(S8900_64XC_MUX_BASE_NR + i + 1); + if (!parent[i]) { + ERROR_MSG("Parent adapter (%d) not found\n", + S8900_64XC_MUX_BASE_NR + i + 1); + return -ENODEV; + } + pdata[i].client = i2c_new_dummy(parent[i], pdata[i].reg_addr); + if (!pdata[i].client) { + ERROR_MSG("Fail to create dummy i2c client for addr %d\n", pdata[i].reg_addr); + goto error; + } + } + + ret = sysfs_create_group(&pdev->dev.kobj, &s8900_64xc_cpld_attr_grp); + if (ret) + goto error; + + return 0; + +error: + if (i < CPLD_DEVICE_NUM) { + i2c_put_adapter(parent[i]); + } + i--; + for (; i >= 0; i--) { + if (pdata[i].client) { + i2c_unregister_device(pdata[i].client); + i2c_put_adapter(parent[i]); + } + } + + return -ENODEV; +} + +static int __exit cpld_remove(struct platform_device *pdev) +{ + int i; + struct i2c_adapter *parent = NULL; + struct cpld_platform_data *pdata = pdev->dev.platform_data; + + sysfs_remove_group(&pdev->dev.kobj, &s8900_64xc_cpld_attr_grp); + + if (!pdata) { + ERROR_MSG("Missing platform data\n"); + } else { + for (i = 0; i < CPLD_DEVICE_NUM; i++) { + if (pdata[i].client) { + parent = (pdata[i].client)->adapter; + i2c_unregister_device(pdata[i].client); + i2c_put_adapter(parent); + } + } + } + + return 0; +} + +static struct platform_driver cpld_driver = { + .probe = cpld_probe, + .remove = __exit_p(cpld_remove), + .driver = { + .owner = THIS_MODULE, + .name = "ingrasys-s8900-64xc-cpld", + }, +}; + +static struct platform_driver qsfp_driver = { + .probe = qsfp_device_probe, + .remove = __exit_p(qsfp_device_remove), + //.id_table = qsfp_device_id, + .driver = { + .owner = THIS_MODULE, + .name = "ingrasys-s8900-64xc-sfp", + }, +}; + + +static int __init ingrasys_s8900_64xc_platform_init(void) +{ + int ret = 0; + + DEBUG_PRINT("ingrasysl_s8900_64xc_platform module initialization\n"); + + //mdelay(10000); + + ret = platform_driver_register(&cpld_driver); + if (ret) { + ERROR_MSG("Fail to register cpld driver\n"); + goto error_cpld_driver; + } + ret = platform_device_register(&s8900_64xc_cpld); + if (ret) { + ERROR_MSG("Fail to create cpld device\n"); + goto error_cpld; + } + + ret = platform_driver_register(&qsfp_driver); + if (ret) { + ERROR_MSG("Fail to register sfp driver\n"); + goto error_cpld_driver; + } + + ret = platform_device_register(&s8900_64xc_sfp); + if (ret) { + ERROR_MSG("Fail to create sfp device\n"); + goto error_cpld; + } + + return 0; + +error_cpld: + platform_driver_unregister(&cpld_driver); + platform_driver_unregister(&qsfp_driver); +error_cpld_driver: + return ret; +} + +static void __exit ingrasys_s8900_64xc_platform_exit(void) +{ + platform_device_unregister(&s8900_64xc_sfp); + platform_device_unregister(&s8900_64xc_cpld); + platform_driver_unregister(&qsfp_driver); + platform_driver_unregister(&cpld_driver); +} + +module_init(ingrasys_s8900_64xc_platform_init); +module_exit(ingrasys_s8900_64xc_platform_exit); + +MODULE_DESCRIPTION("Ingrasys S8900-64XC Platform Support"); +MODULE_AUTHOR("Wade He "); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/service/qsfp-monitor.service b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/service/qsfp-monitor.service new file mode 100644 index 000000000000..99c91b98ab52 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/service/qsfp-monitor.service @@ -0,0 +1,15 @@ +[Unit] +Description= This QSFP Monitor service is to setup QSFP SI. +Requires=s8900-64xc-monitor.service +After=s8900-64xc-monitor.service + +[Service] +ExecStart=/usr/sbin/qsfp_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/service/s8900-64xc-monitor.service b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/service/s8900-64xc-monitor.service new file mode 100644 index 000000000000..decb0f943a9d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/service/s8900-64xc-monitor.service @@ -0,0 +1,18 @@ +[Unit] +Description= This Platform Monitor service is to initialize platform and monitor platform. +Before=platform-monitor.service +After=sysinit.target +Wants=fancontrol.service +DefaultDependencies=no + +[Service] +ExecStartPre=/usr/sbin/i2c_utils.sh i2c_init +ExecStart=/usr/sbin/s8900_64xc_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/utils/i2c_utils.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/utils/i2c_utils.sh new file mode 100644 index 000000000000..cd33df48bd4f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/utils/i2c_utils.sh @@ -0,0 +1,1157 @@ +#!/bin/bash + +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +VERSION="1.0.0" +TRUE=200 +FALSE=404 + +EXEC_FUNC=${1} +COLOR_LED=${2} +QSFP_PORT=${2} +ONOFF_LED=${3} +FAN_TRAY=${4} + +############################################################ +# Distributor ID: Debian +# Description: Debian GNU/Linux 8.6 (jessie) +# Release: 8.6 +# Codename: jessie +# Linux debian 3.16.0-4-amd64 #1 +# SMP Debian 3.16.36-1+deb8u1 (2016-09-03) x86_64 GNU/Linux +############################################################ + +# Color Definition +COLOR_TITLE="\e[1;32m" ### Green ### +COLOR_WARNING="\e[1;33m" ### Yellow ### +COLOR_ERROR="\e[1;31m" ### Red ### +COLOR_END="\e[0m" ### END ### + +NUM_IGB_DEVICE=0 +NUM_I801_DEVICE=0 +NUM_ISMT_DEVICE=$(( ${NUM_I801_DEVICE} + 1 )) +NUM_MUX1_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 2 )) +NUM_MUX1_CHAN1_DEVICE=$(( ${NUM_I801_DEVICE} + 3 )) +NUM_MUX1_CHAN2_DEVICE=$(( ${NUM_I801_DEVICE} + 4 )) +NUM_MUX1_CHAN3_DEVICE=$(( ${NUM_I801_DEVICE} + 5 )) +NUM_MUX1_CHAN4_DEVICE=$(( ${NUM_I801_DEVICE} + 6 )) +NUM_MUX1_CHAN5_DEVICE=$(( ${NUM_I801_DEVICE} + 7 )) +NUM_MUX1_CHAN6_DEVICE=$(( ${NUM_I801_DEVICE} + 8 )) +NUM_MUX1_CHAN7_DEVICE=$(( ${NUM_I801_DEVICE} + 9 )) + +PATH_SYS_I2C_DEVICES="/sys/bus/i2c/devices" +PATH_HWMON_ROOT_DEVICES="/sys/class/hwmon" +PATH_HWMON_W83795_DEVICE="${PATH_HWMON_ROOT_DEVICES}/hwmon1" +PATH_I801_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_I801_DEVICE}" +PATH_ISMT_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_ISMT_DEVICE}" +PATH_MUX_CHAN0_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN0_DEVICE}" +PATH_MUX_CHAN1_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN1_DEVICE}" +PATH_MUX_CHAN2_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN2_DEVICE}" +PATH_MUX_CHAN3_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN3_DEVICE}" +PATH_MUX_CHAN4_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN4_DEVICE}" +PATH_MUX_CHAN5_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN5_DEVICE}" +PATH_MUX_CHAN6_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN6_DEVICE}" +PATH_MUX_CHAN7_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN7_DEVICE}" + +#Power Supply Status +PSU_DC_ON=1 +PSU_DC_OFF=0 +PSU_EXIST=1 +PSU_NOT_EXIST=0 + +# Help usage function +function _help { + echo "=========================================================" + echo "# Description: Help Function" + echo "=========================================================" + echo "----------------------------------------------------" + echo "EX : ${0} help" + echo " : ${0} i2c_init" + echo " : ${0} i2c_temp_init" + echo " : ${0} i2c_fan_init" + echo " : ${0} i2c_volmon_init" + echo " : ${0} i2c_io_exp_init" + echo " : ${0} i2c_led_test" + echo " : ${0} i2c_psu_eeprom_get" + echo " : ${0} i2c_mb_eeprom_get" + echo " : ${0} i2c_qsfp_eeprom_get [1-64]" + echo " : ${0} i2c_qsfp_status_get [1-64]" + echo " : ${0} i2c_qsfp_type_get [1-64]" + echo " : ${0} i2c_qsfp_abs_get [1-64]" + echo " : ${0} i2c_qsfp_rst_get [49-64]" + echo " : ${0} i2c_qsfp_lpmode_get [49-64]" + echo " : ${0} i2c_board_type_get" + echo " : ${0} i2c_psu_status" + echo " : ${0} i2c_led_psu_status_set" + echo " : ${0} i2c_led_fan_status_set" + echo " : ${0} i2c_led_fan_tray_status_set" + echo " : ${0} i2c_cpld_version" + echo " : ${0} i2c_front_temp" + echo " : ${0} i2c_rear_temp" + echo " : ${0} i2c_test_all" + echo " : ${0} i2c_sys_led green|amber on|off" + echo " : ${0} i2c_fan_led green|amber on|off" + echo " : ${0} i2c_psu1_led green|amber on|off" + echo " : ${0} i2c_psu2_led green|amber on|off" + echo " : ${0} i2c_fan_tray_led green|amber on|off [1-5]" + echo "----------------------------------------------------" +} + +#Pause function +function _pause { + read -p "$*" +} + +#Retry command function +function _retry { + for i in {1..5}; + do + eval "${*}" && break || echo "retry"; sleep 1; + done +} + + +#I2C Init +function _i2c_init { + echo "=========================================================" + echo "# Description: I2C Init" + echo "=========================================================" + + rmmod i2c_ismt + rmmod i2c_i801 + modprobe i2c_i801 + modprobe i2c_ismt + modprobe i2c_dev + modprobe i2c_mux_pca954x force_deselect_on_exit=1 + + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x72' > ${PATH_ISMT_DEVICE}/new_device" + else + echo "pca9548 0x72 already init." + fi + #Init CPLD LED_CLR Register (Front Port LED) + i2cset -y ${NUM_I801_DEVICE} 0x33 0x34 0x10 + + rmmod coretemp + rmmod jc42 + rmmod w83795 + rmmod lm75 + _i2c_temp_init + _i2c_volmon_init + _i2c_hwmon_init + modprobe coretemp + modprobe w83795 + modprobe lm75 + modprobe jc42 + modprobe sff_8436_eeprom + modprobe eeprom + modprobe eeprom_mb + modprobe qsfp_cpld + _i2c_fan_init + _i2c_io_exp_init + _i2c_cpld_init + _i2c_psu_eeprom_init + _i2c_led_psu_status_set + _i2c_led_fan_status_set + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_sys_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_sys_led + + echo "tmp75 0x48" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN3_DEVICE}/new_device + echo "tmp75 0x49" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN3_DEVICE}/new_device + echo "Mount Main Board EEPROM" + echo "mb_eeprom 0x54" > /sys/bus/i2c/devices/i2c-7/new_device + + _config_rmem +} + +#I2C Deinit +function _i2c_deinit { + for mod in coretemp jc42 w83795 lm75 eeprom_mb i2c_mux_pca954x i2c_ismt i2c_i801; + do + [ "$(lsmod | grep "^$mod ")" != "" ] && rmmod $mod + done +} + +#Temperature sensor Init +function _i2c_temp_init { + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x00 0x80 + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x05 0xFF + echo "TEMP INIT Done" +} + +#FAN Init +function _i2c_fan_init { + echo -n "FAN INIT..." + if [ -e "${PATH_HWMON_W83795_DEVICE}" ]; then + echo 120 > ${PATH_HWMON_W83795_DEVICE}/device/pwm1 + echo 120 > ${PATH_HWMON_W83795_DEVICE}/device/pwm2 + echo "SUCCESS" + else + echo "FAIL" + fi + +} + +#VOLMON Init +function _i2c_volmon_init { + echo -n "VOLMON INIT..." + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x00 0x80 + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x02 0xFF + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x03 0x50 + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x04 0x0A + echo "Done" +} + +#HWMON Init +function _i2c_hwmon_init { + echo -n "HWMON INIT..." + i2cset -y ${NUM_I801_DEVICE} 0x2f 0x00 0x80 + i2cset -y ${NUM_I801_DEVICE} 0x2f 0x06 0xFF + i2cset -y ${NUM_I801_DEVICE} 0x2f 0x07 0x03 + echo "Done" +} + +#IO Expander Init +function _i2c_io_exp_init { + echo "=========================================================" + echo "# Description: I2C IO Expender Init" + echo "=========================================================" + #SMBUS0 IO_EXPENDER + i2cset -y -r ${NUM_I801_DEVICE} 0x27 4 0x00 + i2cset -y -r ${NUM_I801_DEVICE} 0x27 5 0x00 + i2cset -y -r ${NUM_I801_DEVICE} 0x27 2 0x00 + i2cset -y -r ${NUM_I801_DEVICE} 0x27 3 0x00 + i2cset -y -r ${NUM_I801_DEVICE} 0x27 6 0xFF + i2cset -y -r ${NUM_I801_DEVICE} 0x27 7 0xFF + + #LED board after PVT (S8900_IO_EXP_LED_ID) + echo "Init LED IO Expender" + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 3 0x00 + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 6 0x00 + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 7 0xFF + + #PSU I/O (S8900_IO_EXP_PSU_ID) + echo "Init PSU IO Expender" + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x20 2 0x44 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x20 3 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x20 6 0xBB + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x20 7 0xFF + + #FAN I/O (S8900_IO_EXP_FAN_ID) + echo "Init FAN1 IO Expender" + i2cset -y -r ${NUM_MUX1_CHAN3_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN3_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN3_DEVICE} 0x20 2 0x11 + i2cset -y -r ${NUM_MUX1_CHAN3_DEVICE} 0x20 3 0x11 + i2cset -y -r ${NUM_MUX1_CHAN3_DEVICE} 0x20 6 0xCC + i2cset -y -r ${NUM_MUX1_CHAN3_DEVICE} 0x20 7 0xCC + + #FAN I/O (S8900_IO_EXP_FAN_ID) + echo "Init FAN2 IO Expender" + i2cset -y -r ${NUM_MUX1_CHAN3_DEVICE} 0x21 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN3_DEVICE} 0x21 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN3_DEVICE} 0x21 2 0x10 + i2cset -y -r ${NUM_MUX1_CHAN3_DEVICE} 0x21 3 0x00 + i2cset -y -r ${NUM_MUX1_CHAN3_DEVICE} 0x21 6 0xCC + i2cset -y -r ${NUM_MUX1_CHAN3_DEVICE} 0x21 7 0xFF +} + +#I2C CPLD init +function _i2c_cpld_init { + echo "=========================================================" + echo "# Description: I2C CPLD Init..." + echo "=========================================================" + + ## modprobe i2c_cpld + modprobe i2c_cpld + ## Add CPLD device + echo "i2c_cpld 0x33" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_I801_DEVICE}/new_device + + echo "done..." +} +#Set FAN Tray LED +function _i2c_led_fan_tray_status_set { + echo "FAN Tray Status Setup" + #FAN Status set + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN2_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan2_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN4_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan4_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN6_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan6_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + FAN8_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan8_alarm` + FAN9_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan9_alarm` + FAN10_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan10_alarm` + + if [ "${FAN1_ALARM}" == "0" ] && [ "${FAN2_ALARM}" == "0" ]; then + FAN_TRAY=1 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=1 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + if [ "${FAN3_ALARM}" == "0" ] && [ "${FAN4_ALARM}" == "0" ]; then + FAN_TRAY=2 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=2 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + if [ "${FAN5_ALARM}" == "0" ] && [ "${FAN6_ALARM}" == "0" ]; then + FAN_TRAY=3 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=3 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + if [ "${FAN7_ALARM}" == "0" ] && [ "${FAN8_ALARM}" == "0" ]; then + FAN_TRAY=4 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=4 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + if [ "${FAN9_ALARM}" == "0" ] && [ "${FAN10_ALARM}" == "0" ]; then + FAN_TRAY=5 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=5 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi +} + +#Set FAN LED +function _i2c_led_fan_status_set { + echo "FAN Status Setup" + #PSU Status set + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN2_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan2_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN4_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan4_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN6_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan6_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + FAN8_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan8_alarm` + FAN9_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan9_alarm` + FAN10_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan10_alarm` + + if [ "${FAN1_ALARM}" == "0" ] && [ "${FAN2_ALARM}" == "0" ] \ + && [ "${FAN3_ALARM}" == "0" ] && [ "${FAN4_ALARM}" == "0" ] \ + && [ "${FAN5_ALARM}" == "0" ] && [ "${FAN6_ALARM}" == "0" ] \ + && [ "${FAN7_ALARM}" == "0" ] && [ "${FAN8_ALARM}" == "0" ] \ + && [ "${FAN9_ALARM}" == "0" ] && [ "${FAN10_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + fi +} + +#Set Power Supply LED +function _i2c_led_psu_status_set { + echo "PSU LED Status Setup" + + #PSU Status set + _i2c_psu_status + + #PSU1 Status + if [ "${psu1Exist}" == ${PSU_EXIST} ]; then + if [ "${psu1PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + fi + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + fi + + #PSU2 Status + if [ "${psu2Exist}" == ${PSU_EXIST} ]; then + if [ "${psu2PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + fi + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + fi +} + +#LED Test +function _i2c_led_test { + echo "=========================================================" + echo "# Description: I2C LED TEST..." + echo "=========================================================" + #sys led (green) + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xBF + _pause 'Check SYS LED green light and Press [Enter] key to continue...' + #sys led (amber) + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0x7F + _pause 'Check SYS LED amber light and Press [Enter] key to continue...' + + #FAN led (green) + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xEF + _pause 'Check FAN LED green light and Press [Enter] key to continue...' + #FAN led (amber) + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xDF + _pause 'Check FAN LED amber light and Press [Enter] key to continue...' + + #PSU2 led (green) + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFB + _pause 'Check PSU2 LED green light and Press [Enter] key to continue...' + #PSU2 led (amber) + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xF7 + _pause 'Check PSU2 LED amber light and Press [Enter] key to continue...' + + #PSU1 led (green) + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFE + _pause 'Check PSU1 LED green light and Press [Enter] key to continue...' + #PSU1 led (amber) + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFD + _pause 'Check PSU1 LED amber light and Press [Enter] key to continue...' + + #Turn OFF All LED + i2cset -y ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + _pause 'Check turn off all LEDs and Press [Enter] key to continue...' + echo "done..." +} + +#Set QSFP Port variable +function _qsfp_port_i2c_var_set { + local port=$1 + case ${port} in + 1|2|3|4|5|6|7|8) + i2cbus=${NUM_MUX1_CHAN0_DEVICE} + regAddr=0x33 + idx=$(( ((${port} - 1) / 2) )) + dataAddr=$(( (32 + $idx) )) + presentChan=$(( $((${port} % 2)) == 1 ? 0x1 : 0x5 )) + eeprombus=${NUM_MUX1_CHAN0_DEVICE} + eepromidx=${port} + eepromAddr=0x50 + ;; + 9|10|11|12|13|14|15|16) + i2cbus=${NUM_MUX1_CHAN0_DEVICE} + regAddr=0x33 + idx=$(( ((${port} - 1) / 2) )) + dataAddr=$(( (32 + $idx) )) + presentChan=$(( $((${port} % 2)) == 1 ? 0x1 : 0x5 )) + eeprombus=${NUM_MUX1_CHAN0_DEVICE} + eepromidx=${port} + eepromAddr=0x50 + ;; + 17|18|19|20) + i2cbus=${NUM_MUX1_CHAN0_DEVICE} + regAddr=0x33 + idx=$(( ((${port} - 1) / 2) )) + dataAddr=$(( (32 + $idx) )) + presentChan=$(( $((${port} % 2)) == 1 ? 0x1 : 0x5 )) + eeprombus=${NUM_MUX1_CHAN0_DEVICE} + eepromidx=${port} + eepromAddr=0x50 + ;; + 21|22|23|24) + i2cbus=${NUM_MUX1_CHAN0_DEVICE} + regAddr=0x33 + idx=$(( ((${port} - 1) / 2) )) + dataAddr=$(( (38 + $idx) )) + presentChan=$(( $((${port} % 2)) == 1 ? 0x1 : 0x5 )) + eeprombus=${NUM_MUX1_CHAN0_DEVICE} + eepromidx=${port} + eepromAddr=0x50 + ;; + 25|26|27|28|29|30|31|32) + i2cbus=${NUM_MUX1_CHAN1_DEVICE} + regAddr=0x33 + idx=$(( ((${port} - 25) / 2) )) + dataAddr=$(( (32 + $idx) )) + presentChan=$(( $((${port} % 2)) == 1 ? 0x1 : 0x5 )) + eeprombus=${NUM_MUX1_CHAN1_DEVICE} + eepromidx=$(( (${port} - 24) )) + eepromAddr=0x50 + ;; + 33|34|35|36|37|38|39|40) + i2cbus=${NUM_MUX1_CHAN1_DEVICE} + regAddr=0x33 + idx=$(( ((${port} - 25) / 2) )) + dataAddr=$(( (32 + $idx) )) + presentChan=$(( $((${port} % 2)) == 1 ? 0x1 : 0x5 )) + eeprombus=${NUM_MUX1_CHAN1_DEVICE} + eepromidx=$(( (${port} - 24) )) + eepromAddr=0x50 + ;; + 41|42|43|44) + i2cbus=${NUM_MUX1_CHAN1_DEVICE} + regAddr=0x33 + idx=$(( ((${port} - 25) / 2) )) + dataAddr=$(( (32 + $idx) )) + presentChan=$(( $((${port} % 2)) == 1 ? 0x1 : 0x5 )) + eeprombus=${NUM_MUX1_CHAN1_DEVICE} + eepromidx=$(( (${port} - 24) )) + eepromAddr=0x50 + ;; + 45|46|47|48) + i2cbus=${NUM_MUX1_CHAN1_DEVICE} + regAddr=0x33 + idx=$(( ((${port} - 25) / 2) )) + dataAddr=$(( (38 + $idx) )) + presentChan=$(( $((${port} % 2)) == 1 ? 0x1 : 0x5 )) + eeprombus=${NUM_MUX1_CHAN1_DEVICE} + eepromidx=$(( (${port} - 24) )) + eepromAddr=0x50 + ;; + 49|50|51|52|53|54|55|56) + i2cbus=${NUM_MUX1_CHAN2_DEVICE} + regAddr=0x33 + idx=$(( (${port} - 49) )) + dataAddr=$(( (32 + $idx) )) + rstsellpdataAddr=$(( (48 + $idx) )) + intChan=0x0 + rstChan=0x0 + presentChan=0x1 + modselChan=0x1 + lpmodeChan=0x2 + eeprombus=${NUM_MUX1_CHAN2_DEVICE} + eepromidx=$(( (${port} - 48) )) + intabsidx=$(( (${port} - 28) )) + rstsellpidx=$(( (${port} - 18) )) + eepromAddr=0x50 + ;; + 57|58|59|60|61|62|63|64) + i2cbus=${NUM_MUX1_CHAN2_DEVICE} + regAddr=0x33 + idx=$(( (${port} - 49) )) + dataAddr=$(( (32 + $idx) )) + rstsellpdataAddr=$(( (48 + $idx) )) + intChan=0x0 + rstChan=0x0 + presentChan=0x1 + modselChan=0x1 + lpmodeChan=0x2 + eeprombus=${NUM_MUX1_CHAN2_DEVICE} + eepromidx=$(( (${port} - 48) )) + intabsidx=$(( (${port} - 28) )) + rstsellpidx=$(( (${port} - 18) )) + eepromAddr=0x50 + ;; + *) + echo "Please input 1~64" + exit + ;; + esac +} + +#Get SFP/QSFP EEPROM Information +function _i2c_qsfp_eeprom_get { + _qsfp_port_i2c_var_set ${QSFP_PORT} + + regData=`cat /sys/devices/platform/ingrasys-s8900-64xc-cpld.0/qsfp_modprs` + port_num=$(( ${QSFP_PORT} - 1 )) + + #status: 0 -> Down, 1 -> Up + status=$(( $(($regData & ( 1 << $port_num))) != 0 ? 0 : 1 )) + + + if [ $status = 0 ]; then + echo "Module not present." + exit + fi + + #Read SFP/QSFP EEPROM + if [ ${QSFP_PORT} -lt 49 ]; then + cat ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/sfp${QSFP_PORT} | hexdump -C + elif [ ${QSFP_PORT} -ge 49 ]; then + cat ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/qsfp${QSFP_PORT} | hexdump -C + fi +} + +#Get QSFP Status for QSFP monitor service +function _i2c_qsfp_status_get { + _i2c_qsfp_abs_get + echo "status=$status" +} + +#Get QSFP type for QSFP monitor server +function _i2c_qsfp_type_get { + _qsfp_port_i2c_var_set ${QSFP_PORT} + + regData=`cat /sys/devices/platform/ingrasys-s8900-64xc-cpld.0/qsfp_modprs` + port_num=$(( ${QSFP_PORT} - 1 )) + + #status: 0 -> Down, 1 -> Up + status=$(( $(($regData & ( 1 << $port_num))) != 0 ? 0 : 1 )) + + if [ $status = 0 ]; then + echo "Module not present." + exit + fi + + #Read SFP/QSFP EEPROM + if [ ${QSFP_PORT} -lt 49 ]; then + qsfp_info=$(base64 ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/sfp${QSFP_PORT}) + elif [ ${QSFP_PORT} -ge 49 ]; then + qsfp_info=$(base64 ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/qsfp${QSFP_PORT}) + fi + + if [ ${QSFP_PORT} -ge 1 ] && [ ${QSFP_PORT} -le 48 ]; then + echo "sfp" + # 1~48 port is sfp port + identifier=$(echo $qsfp_info | base64 -d -i | hexdump -s 0 -n 1 -e '"%x"') + connector=$(echo $qsfp_info | base64 -d -i | hexdump -s 2 -n 1 -e '"%x"') + transceiver=$(echo $qsfp_info | base64 -d -i | hexdump -s 3 -n 1 -e '"%x"') + else + echo "qsfp" + # 49~64 port is qsfp port + identifier=$(echo $qsfp_info | base64 -d -i | hexdump -s 128 -n 1 -e '"%x"') + connector=$(echo $qsfp_info | base64 -d -i | hexdump -s 130 -n 1 -e '"%x"') + transceiver=$(echo $qsfp_info | base64 -d -i | hexdump -s 131 -n 1 -e '"%x"') + fi + + echo "identifier=$identifier" + echo "connector=$connector" + echo "transceiver=$transceiver" +} + +#Get QSFP ABS +function _i2c_qsfp_abs_get { + _qsfp_port_i2c_var_set ${QSFP_PORT} + + regData=`cat /sys/devices/platform/ingrasys-s8900-64xc-cpld.0/qsfp_modprs` + port_num=$(( ${QSFP_PORT} - 1 )) + + #status: 0 -> Down, 1 -> Up + status=$(( $(($regData & ( 1 << $port_num))) != 0 ? 0 : 1 )) + echo "ABS=$status regData=$regData" + + +} + +#Get QSFP RST +function _i2c_qsfp_rst_get { + + if [ ${QSFP_PORT} -lt 49 ] || [ ${QSFP_PORT} -gt 64 ]; then + echo "Please input 49~64" + exit + fi + _qsfp_port_i2c_var_set ${QSFP_PORT} + + + regData=`cat /sys/devices/platform/ingrasys-s8900-64xc-cpld.0/qsfp_reset` + port_num=$(( ${QSFP_PORT} - 49 )) + + #status: 0 -> Down, 1 -> Up + status=$(( $(($regData & ( 1 << $port_num))) != 0 ? 0 : 1 )) + echo "RST=$status regData=$regData" + +} + +#Get QSFP LPMODE +function _i2c_qsfp_lpmode_get { + + if [ ${QSFP_PORT} -lt 49 ] || [ ${QSFP_PORT} -gt 64 ]; then + echo "Please input 49~64" + exit + fi + _qsfp_port_i2c_var_set ${QSFP_PORT} + + + regData=`cat /sys/devices/platform/ingrasys-s8900-64xc-cpld.0/qsfp_lpmode` + port_num=$(( ${QSFP_PORT} - 49 )) + + #status: 0 -> Down, 1 -> Up + status=$(( $(($regData & ( 1 << $port_num))) != 0 ? 1 : 0 )) + echo "LPMODE=$status regData=$regData" + +} +#PSU EEPROM init +function _i2c_psu_eeprom_init { + echo "=========================================================" + echo "# Description: I2C PSU EEPROM Init..." + echo "=========================================================" + + ## modprobe eeprom + modprobe eeprom + ## PUS(0) EEPROM + echo "eeprom 0x50" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN6_DEVICE}/new_device + + ## PUS(1) EEPROM + echo "eeprom 0x50" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN7_DEVICE}/new_device + + echo "done..." +} + +#Get PSU EEPROM Information +function _i2c_psu_eeprom_get { + echo "=========================================================" + echo "# Description: I2C PSU EEPROM Get..." + echo "=========================================================" + + ## modprobe eeprom + modprobe eeprom + ## PUS(0) EEPROM + cat ${PATH_SYS_I2C_DEVICES}/${NUM_MUX1_CHAN6_DEVICE}-0050/eeprom | hexdump -C + + ## PUS(1) EEPROM + cat ${PATH_SYS_I2C_DEVICES}/${NUM_MUX1_CHAN7_DEVICE}-0050/eeprom | hexdump -C + echo "done..." +} + +#Get MotherBoard EEPROM Information +function _i2c_mb_eeprom_get { + echo "=========================================================" + echo "# Description: I2C MB EEPROM Get..." + echo "=========================================================" + + ## modprobe eeprom + modprobe eeprom_mb + + ## MB EEPROM + cat ${PATH_SYS_I2C_DEVICES}/${NUM_MUX1_CHAN5_DEVICE}-0054/eeprom | hexdump -C + echo "done..." +} + +#Set System Status LED +function _i2c_sys_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x40 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x40 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x80 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x80 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Set PSU2 LED +function _i2c_psu2_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x04 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x04 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x08 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x08 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Set FAN Tray LED +function _i2c_fan_tray_led { + + case ${FAN_TRAY} in + 1) + i2cAddr=0x20 + ioPort=2 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x01 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x02 + fi + ;; + 2) + i2cAddr=0x20 + ioPort=2 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + 3) + i2cAddr=0x20 + ioPort=3 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x01 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x02 + fi + ;; + 4) + i2cAddr=0x20 + ioPort=3 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + 5) + i2cAddr=0x21 + ioPort=2 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + *) + echo "Please input 1~5" + exit + ;; + esac + + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m $mask -y -r ${NUM_MUX1_CHAN3_DEVICE} $i2cAddr $ioPort 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m $mask -y -r ${NUM_MUX1_CHAN3_DEVICE} $i2cAddr $ioPort 0x33 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m $mask -y -r ${NUM_MUX1_CHAN3_DEVICE} $i2cAddr $ioPort 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m $mask -y -r ${NUM_MUX1_CHAN3_DEVICE} $i2cAddr $ioPort 0x33 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Set FAN LED +function _i2c_fan_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x10 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x10 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x20 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x20 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Set PSU1 LED +function _i2c_psu1_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x01 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x01 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x02 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x02 -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 2 0xFF + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Get Board Version and Type +function _i2c_board_type_get { + boardType=`cat ${PATH_SYS_I2C_DEVICES}/0-0033/cpld_board_type` + boardBuildRev=$((($boardType) & 0x03)) + boardHwRev=$((($boardType) >> 2 & 0x03)) + boardId=$((($boardType) >> 4)) + printf "BOARD_ID is 0x%02x, HW Rev %d, Build Rev %d\n" $boardId $boardHwRev $boardBuildRev + +} + +#Get CPLD Version +function _i2c_cpld_version { + cpldRev=`cat ${PATH_SYS_I2C_DEVICES}/0-0033/cpld_version` + cpldRelease=$((($cpldRev) >> 6 & 0x01)) + cpldVersion=$((($cpldRev) & 0x3F)) + printf "CPLD is %s version(0:RD 1:Release), Revision is 0x%02x\n" $cpldRelease $cpldVersion + +} + +#Get PSU Status +function _i2c_psu_status { + psuPresent=`cat ${PATH_SYS_I2C_DEVICES}/0-0033/cpld_pw_abs` + psu1Exist=$(($((($psuPresent) & 0x01))?0:1)) + psu2Exist=$(($((($psuPresent) & 0x02))?0:1)) + psuPwGood=`cat ${PATH_SYS_I2C_DEVICES}/0-0033/cpld_pw_good` + psu1PwGood=$(($((($psuPwGood) >> 2 & 0x01))?1:0)) + psu2PwGood=$(($((($psuPwGood) >> 2 & 0x02))?1:0)) + printf "PSU1 Exist:%d PSU1 PW Good:%d\n" $psu1Exist $psu1PwGood + printf "PSU2 Exist:%d PSU2 PW Good:%d\n" $psu2Exist $psu2PwGood +} + +#Get Front Sensor Temperature +function _i2c_front_temp { + #Front MAC + sensors | grep 'Front MAC Temp' -A 1 +} + +#Get Rear Sensor Temperature +function _i2c_rear_temp { + #Rear MAC + sensors | grep 'Rear MAC Temp' -A 1 +} + +#Increase read socket buffer for CoPP Test +function _config_rmem { + echo "109430400" > /proc/sys/net/core/rmem_max +} + +#Main Function +function _main { + tart_time_str=`date` + start_time_sec=$(date +%s) + + if [ "${EXEC_FUNC}" == "help" ]; then + _help + elif [ "${EXEC_FUNC}" == "i2c_init" ]; then + _i2c_init + elif [ "${EXEC_FUNC}" == "i2c_deinit" ]; then + _i2c_deinit + elif [ "${EXEC_FUNC}" == "i2c_temp_init" ]; then + _i2c_temp_init + elif [ "${EXEC_FUNC}" == "i2c_fan_init" ]; then + _i2c_fan_init + elif [ "${EXEC_FUNC}" == "i2c_volmon_init" ]; then + _i2c_volmon_init + elif [ "${EXEC_FUNC}" == "i2c_io_exp_init" ]; then + _i2c_io_exp_init + elif [ "${EXEC_FUNC}" == "i2c_led_test" ]; then + _i2c_led_test + elif [ "${EXEC_FUNC}" == "i2c_mb_eeprom_get" ]; then + _i2c_mb_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_psu_eeprom_get" ]; then + _i2c_psu_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_eeprom_get" ]; then + _i2c_qsfp_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_status_get" ]; then + _i2c_qsfp_status_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_type_get" ]; then + _i2c_qsfp_type_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_abs_get" ]; then + _i2c_qsfp_abs_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_rst_get" ]; then + _i2c_qsfp_rst_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_lpmode_get" ]; then + _i2c_qsfp_lpmode_get + elif [ "${EXEC_FUNC}" == "i2c_led_psu_status_set" ]; then + _i2c_led_psu_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_status_set" ]; then + _i2c_led_fan_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_tray_status_set" ]; then + _i2c_led_fan_tray_status_set + elif [ "${EXEC_FUNC}" == "i2c_sys_led" ]; then + _i2c_sys_led + elif [ "${EXEC_FUNC}" == "i2c_fan_led" ]; then + _i2c_fan_led + elif [ "${EXEC_FUNC}" == "i2c_fan_tray_led" ]; then + _i2c_fan_tray_led + elif [ "${EXEC_FUNC}" == "i2c_psu1_led" ]; then + _i2c_psu1_led + elif [ "${EXEC_FUNC}" == "i2c_psu2_led" ]; then + _i2c_psu2_led + elif [ "${EXEC_FUNC}" == "i2c_board_type_get" ]; then + _i2c_board_type_get + elif [ "${EXEC_FUNC}" == "i2c_cpld_version" ]; then + _i2c_cpld_version + elif [ "${EXEC_FUNC}" == "i2c_psu_status" ]; then + _i2c_psu_status + elif [ "${EXEC_FUNC}" == "i2c_front_temp" ]; then + _i2c_front_temp + elif [ "${EXEC_FUNC}" == "i2c_rear_temp" ]; then + _i2c_rear_temp + elif [ "${EXEC_FUNC}" == "i2c_test_all" ]; then + _i2c_init + _i2c_temp_init + _i2c_fan_init + _i2c_io_exp_init + _i2c_led_test + _i2c_psu_eeprom_get + _i2c_mb_eeprom_get + _i2c_board_type_get + _i2c_cpld_version + _i2c_psu_status + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + end_time_str=`date` + end_time_sec=$(date +%s) + diff_time=$[ ${end_time_sec} - ${start_time_sec} ] + echo "Start Time: ${start_time_str} (${start_time_sec})" + echo "End Time : ${end_time_str} (${end_time_sec})" + echo "Total Execution Time: ${diff_time} sec" + + echo "done!!!" +} + +_main diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/utils/qsfp_monitor.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/utils/qsfp_monitor.sh new file mode 100644 index 000000000000..36f9e53ef108 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/utils/qsfp_monitor.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# Copyright (C) 2017 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=3 +I2C_UTILS="/usr/sbin/i2c_utils.sh" +QSFP_SI_SCRIPT="/usr/sbin/qsfp_si_cfg.sh" +QSFP_ARRAY=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \ + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + +#QSFP SI monitor +function _qsfp_si_monitor { + local i + local status + for i in {0..63}; + do + status=`${I2C_UTILS} i2c_qsfp_status_get $(expr $i + 1) | egrep '^status=.*$' | sed -e 's/status=//g'` + if [ "${status}" == "1" ]; then + _qsfp_type_check $i + fi + done +} + +#QSFP type +function _qsfp_type_check { + local port=$1 + local qsfp_type=`${I2C_UTILS} i2c_qsfp_type_get $(expr $port + 1)` + local identifier=`echo "$qsfp_type" | grep '^identifier=.*$' | sed -e 's/identifier=//g'` + # identifier 11 for qsfp, 3 for sfp + if [ "${identifier}" == "11" ] || [ "${identifier}" == "3" ]; then + connector=`echo "$qsfp_type" | grep '^connector=.*$' | sed -e 's/connector=//g'` + case ${connector} in + 21|23) + #DAC + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to DAC" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} dac $port >/dev/null + fi + ;; + *) + #AOC + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to AOC" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} aoc $port >/dev/null + fi + ;; + esac + fi +} + +#Docker exist check +function _docker_swss_check { + while true + do + # Check if syncd starts + result=`docker exec -i swss bash -c "echo -en \"SELECT 1\\nHLEN HIDDEN\" | redis-cli | sed -n 2p"` #TBD FIX ME + if [ "$result" == "3" ]; then + return + fi + sleep $INTERVAL + done +} + +#Docker exist check +function _qsfp_si_cfg_script_check { + + if [ -f ${QSFP_SI_SCRIPT} ] && [ -x ${QSFP_SI_SCRIPT} ]; then + echo "SI Script exists. Start monitor." + return + else + echo "SI Script not exist. Exit monitor." + exit + fi +} + +# main function +function _main { + #Check SI Script + _qsfp_si_cfg_script_check + #Check docker swss is running + _docker_swss_check + while true + do + _qsfp_si_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/utils/qsfp_si_cfg.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/utils/qsfp_si_cfg.sh new file mode 100644 index 000000000000..3d132641f516 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/utils/qsfp_si_cfg.sh @@ -0,0 +1,409 @@ +#!/bin/bash +# Copyright (C) 2017 Ingrasys, Inc. +# +# 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 3 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, see . + +VERSION="1.0.0" +TRUE=200 +FALSE=404 + +TYPE=${1} +PORT=${2} + +# port number +MIN_PORT=0 +MAX_PORT=63 + +# INDEX of value field in array +SI_PRE_IDX=1 +SI_POST_IDX=2 +SI_MAIN_IDX=3 +SI_AMP_IDX=4 +SI_DRI_IDX=5 + +# type string +AOC_TYPE="aoc" +DAC_TYPE="dac" + +# si value for bcm command +SI_VAL="" + +# "PRE POST MAIN AMP DRIVERMODE" for each port in array +xe_aoc_si_value=( + "0x00 0x30 0x40 0x9 0x0" #port 0 + "0x00 0x30 0x40 0x9 0x0" #port 1 + "0x00 0x30 0x40 0x9 0x0" #port 2 + "0x00 0x2c 0x44 0x9 0x0" #port 3 + "0x00 0x30 0x40 0x9 0x0" #port 4 + "0x00 0x30 0x40 0x9 0x0" #port 5 + "0x00 0x2c 0x44 0x9 0x0" #port 6 + "0x00 0x2c 0x44 0x9 0x0" #port 7 + "0x00 0x2c 0x44 0x9 0x0" #port 8 + "0x00 0x28 0x48 0x9 0x0" #port 9 + "0x00 0x2c 0x44 0x9 0x0" #port 10 + "0x00 0x2c 0x44 0x9 0x0" #port 11 + "0x00 0x2c 0x44 0x9 0x0" #port 12 + "0x00 0x2c 0x44 0x9 0x0" #port 13 + "0x00 0x2c 0x44 0x9 0x0" #port 14 + "0x00 0x2c 0x44 0x9 0x0" #port 15 + "0x00 0x2c 0x44 0xb 0x0" #port 16 + "0x00 0x30 0x40 0xb 0x0" #port 17 + "0x00 0x2c 0x44 0xa 0x0" #port 18 + "0x00 0x2c 0x44 0x9 0x0" #port 19 + "0x00 0x30 0x40 0xa 0x0" #port 20 + "0x00 0x2c 0x44 0x9 0x0" #port 21 + "0x00 0x2c 0x44 0x9 0x0" #port 22 + "0x00 0x30 0x40 0xc 0x0" #port 23 + "0x00 0x30 0x40 0xc 0x0" #port 24 + "0x00 0x2c 0x44 0x9 0x0" #port 25 + "0x00 0x30 0x40 0x9 0x0" #port 26 + "0x00 0x30 0x40 0x9 0x0" #port 27 + "0x00 0x30 0x40 0xa 0x0" #port 28 + "0x00 0x28 0x48 0x9 0x0" #port 29 + "0x00 0x2c 0x44 0x9 0x0" #port 30 + "0x00 0x2c 0x44 0xa 0x0" #port 31 + "0x00 0x28 0x48 0x9 0x0" #port 32 + "0x00 0x28 0x48 0x7 0x0" #port 33 + "0x00 0x28 0x48 0x9 0x0" #port 34 + "0x00 0x28 0x48 0x9 0x0" #port 35 + "0x00 0x28 0x48 0x9 0x0" #port 36 + "0x00 0x2c 0x44 0x9 0x0" #port 37 + "0x00 0x28 0x48 0x9 0x0" #port 38 + "0x00 0x28 0x48 0x9 0x0" #port 39 + "0x00 0x28 0x48 0xa 0x0" #port 40 + "0x00 0x2c 0x44 0x9 0x0" #port 41 + "0x00 0x2c 0x44 0x9 0x0" #port 42 + "0x00 0x2e 0x42 0xa 0x0" #port 43 + "0x00 0x2e 0x42 0xa 0x0" #port 44 + "0x00 0x2c 0x44 0xa 0x0" #port 45 + "0x00 0x2c 0x44 0x9 0x0" #port 46 + "0x00 0x2c 0x44 0x9 0x0" #port 47 +) + +ce_aoc_si_value=( + "0x00 0x2c 0x44 0x9 0x0" #port 0 lane 0 + "0x00 0x34 0x3c 0xf 0x0" #port 0 lane 1 + "0x00 0x2c 0x44 0x9 0x0" #port 0 lane 2 + "0x00 0x2c 0x44 0x9 0x0" #port 0 lane 3 + "0x00 0x28 0x48 0x9 0x0" #port 1 lane 0 + "0x00 0x28 0x48 0x9 0x0" #port 1 lane 1 + "0x00 0x2c 0x44 0x9 0x0" #port 1 lane 2 + "0x00 0x2c 0x44 0xe 0x0" #port 1 lane 3 + "0x00 0x28 0x48 0x9 0x0" #port 2 lane 0 + "0x00 0x34 0x3c 0xf 0x0" #port 2 lane 1 + "0x00 0x28 0x48 0x9 0x0" #port 2 lane 2 + "0x00 0x30 0x40 0xd 0x0" #port 2 lane 3 + "0x00 0x2c 0x44 0x9 0x0" #port 3 lane 0 + "0x00 0x30 0x40 0xd 0x0" #port 3 lane 1 + "0x00 0x2c 0x44 0x9 0x0" #port 3 lane 2 + "0x00 0x30 0x40 0xd 0x0" #port 3 lane 3 + "0x00 0x2c 0x44 0x9 0x0" #port 4 lane 0 + "0x00 0x2c 0x44 0x9 0x0" #port 4 lane 1 + "0x00 0x2c 0x44 0x9 0x0" #port 4 lane 2 + "0x00 0x30 0x40 0xd 0x0" #port 4 lane 3 + "0x00 0x2c 0x44 0x9 0x0" #port 5 lane 0 + "0x00 0x38 0x38 0xf 0x0" #port 5 lane 1 + "0x00 0x2c 0x44 0x9 0x0" #port 5 lane 2 + "0x00 0x2c 0x44 0x9 0x0" #port 5 lane 3 + "0x00 0x2c 0x44 0x9 0x0" #port 6 lane 0 + "0x00 0x2c 0x44 0x9 0x0" #port 6 lane 1 + "0x00 0x2c 0x44 0x9 0x0" #port 6 lane 2 + "0x00 0x30 0x40 0xe 0x0" #port 6 lane 3 + "0x00 0x28 0x48 0x9 0x0" #port 7 lane 0 + "0x00 0x30 0x40 0xa 0x0" #port 7 lane 1 + "0x00 0x2c 0x44 0x9 0x0" #port 7 lane 2 + "0x00 0x34 0x3c 0xe 0x0" #port 7 lane 3 + "0x00 0x2c 0x44 0x9 0x0" #port 8 lane 0 + "0x00 0x30 0x40 0xe 0x0" #port 8 lane 1 + "0x00 0x34 0x3c 0xf 0x0" #port 8 lane 2 + "0x00 0x30 0x40 0xe 0x0" #port 8 lane 3 + "0x00 0x2c 0x44 0x9 0x0" #port 9 lane 0 + "0x00 0x34 0x3c 0xd 0x0" #port 9 lane 1 + "0x00 0x34 0x3c 0xd 0x0" #port 9 lane 2 + "0x00 0x34 0x3c 0xf 0x0" #port 9 lane 3 + "0x00 0x34 0x3c 0xd 0x0" #port 10 lane 0 + "0x00 0x34 0x3c 0xe 0x0" #port 10 lane 1 + "0x00 0x34 0x3c 0xd 0x0" #port 10 lane 2 + "0x00 0x34 0x3c 0xd 0x0" #port 10 lane 3 + "0x00 0x34 0x3c 0xd 0x0" #port 11 lane 0 + "0x00 0x34 0x3c 0xd 0x0" #port 11 lane 1 + "0x00 0x30 0x40 0xd 0x0" #port 11 lane 2 + "0x00 0x30 0x40 0xd 0x0" #port 11 lane 3 + "0x00 0x34 0x3c 0xd 0x0" #port 12 lane 0 + "0x00 0x34 0x3c 0xd 0x0" #port 12 lane 1 + "0x00 0x34 0x3c 0xd 0x0" #port 12 lane 2 + "0x00 0x34 0x3c 0xd 0x0" #port 12 lane 3 + "0x00 0x34 0x3c 0xd 0x0" #port 13 lane 0 + "0x00 0x34 0x3c 0xd 0x0" #port 13 lane 1 + "0x00 0x34 0x3c 0xd 0x0" #port 13 lane 2 + "0x00 0x34 0x3c 0xf 0x0" #port 13 lane 3 + "0x00 0x38 0x38 0xe 0x0" #port 14 lane 0 + "0x00 0x34 0x3c 0xd 0x0" #port 14 lane 1 + "0x00 0x34 0x3c 0xd 0x0" #port 14 lane 2 + "0x00 0x38 0x38 0xe 0x0" #port 14 lane 3 + "0x00 0x34 0x3c 0xd 0x0" #port 15 lane 0 + "0x00 0x30 0x40 0xc 0x0" #port 15 lane 1 + "0x00 0x30 0x40 0xc 0x0" #port 15 lane 2 + "0x00 0x34 0x3c 0xd 0x0" #port 15 lane 3 +) + +xe_dac_si_value=( + "0x14 0x00 0x3c 0xf 0x0" #port 0 + "0x1c 0x00 0x3c 0xf 0x0" #port 1 + "0x08 0x00 0x3c 0xf 0x0" #port 2 + "0x08 0x00 0x3c 0xf 0x0" #port 3 + "0x08 0x00 0x3c 0xf 0x0" #port 4 + "0x10 0x00 0x3c 0xf 0x0" #port 5 + "0x08 0x08 0x3c 0xf 0x0" #port 6 + "0x10 0x04 0x3c 0xf 0x0" #port 7 + "0x08 0x00 0x3c 0x9 0x0" #port 8 + "0x08 0x00 0x3c 0x9 0x0" #port 9 + "0x08 0x00 0x3c 0x9 0x0" #port 10 + "0x08 0x00 0x3c 0x9 0x0" #port 11 + "0x08 0x00 0x3c 0x9 0x0" #port 12 + "0x08 0x00 0x3c 0x9 0x0" #port 13 + "0x08 0x00 0x3c 0x9 0x0" #port 14 + "0x08 0x00 0x3c 0x9 0x0" #port 15 + "0x08 0x0c 0x3c 0xb 0x0" #port 16 + "0x10 0x0c 0x3c 0xb 0x0" #port 17 + "0x08 0x08 0x3c 0xa 0x0" #port 18 + "0x10 0x0c 0x3c 0x9 0x0" #port 19 + "0x08 0x04 0x3c 0xc 0x0" #port 20 + "0x08 0x08 0x3c 0x9 0x0" #port 21 + "0x08 0x08 0x3c 0xc 0x0" #port 22 + "0x10 0x08 0x3c 0xa 0x0" #port 23 + "0x08 0x08 0x3c 0x9 0x0" #port 24 + "0x08 0x04 0x3c 0x9 0x0" #port 25 + "0x08 0x00 0x3c 0x9 0x0" #port 26 + "0x08 0x08 0x3c 0xc 0x0" #port 27 + "0x10 0x00 0x3c 0xa 0x0" #port 28 + "0x10 0x04 0x3c 0x9 0x0" #port 29 + "0x14 0x00 0x3c 0x9 0x0" #port 30 + "0x14 0x00 0x3c 0xa 0x0" #port 31 + "0x08 0x00 0x3c 0x9 0x0" #port 32 + "0x08 0x00 0x3c 0x7 0x0" #port 33 + "0x08 0x00 0x3c 0x9 0x0" #port 34 + "0x08 0x00 0x3c 0x9 0x0" #port 35 + "0x08 0x00 0x3c 0x9 0x0" #port 36 + "0x0c 0x00 0x3c 0x9 0x0" #port 37 + "0x08 0x00 0x3c 0x9 0x0" #port 38 + "0x08 0x00 0x3c 0x9 0x0" #port 39 + "0x10 0x00 0x3c 0xa 0x0" #port 40 + "0x10 0x00 0x3c 0x9 0x0" #port 41 + "0x08 0x08 0x3c 0xc 0x0" #port 42 + "0x10 0x00 0x3c 0xa 0x0" #port 43 + "0x08 0x00 0x3c 0xa 0x0" #port 44 + "0x10 0x00 0x3c 0x9 0x0" #port 45 + "0x08 0x04 0x3c 0x9 0x0" #port 46 + "0x08 0x00 0x3c 0xa 0x0" #port 47 +) + +ce_dac_si_value=( + "0x08 0x00 0x3c 0x9 0x0" #port 0 lane 0 + "0x08 0x00 0x3c 0xf 0x0" #port 0 lane 1 + "0x08 0x00 0x3c 0x9 0x0" #port 0 lane 2 + "0x08 0x00 0x3c 0x9 0x0" #port 0 lane 3 + "0x08 0x00 0x3c 0x9 0x0" #port 1 lane 0 + "0x08 0x00 0x3c 0x9 0x0" #port 1 lane 1 + "0x08 0x00 0x3c 0x9 0x0" #port 1 lane 2 + "0x08 0x00 0x3c 0xe 0x0" #port 1 lane 3 + "0x08 0x00 0x3c 0x9 0x0" #port 2 lane 0 + "0x08 0x00 0x3c 0xf 0x0" #port 2 lane 1 + "0x08 0x00 0x3c 0x9 0x0" #port 2 lane 2 + "0x08 0x00 0x3c 0xd 0x0" #port 2 lane 3 + "0x08 0x00 0x3c 0x9 0x0" #port 3 lane 0 + "0x08 0x00 0x3c 0xd 0x0" #port 3 lane 1 + "0x08 0x00 0x3c 0x9 0x0" #port 3 lane 2 + "0x08 0x00 0x3c 0xd 0x0" #port 3 lane 3 + "0x08 0x00 0x3c 0x9 0x0" #port 4 lane 0 + "0x08 0x00 0x3c 0x9 0x0" #port 4 lane 1 + "0x08 0x00 0x3c 0x9 0x0" #port 4 lane 2 + "0x08 0x00 0x3c 0xd 0x0" #port 4 lane 3 + "0x08 0x00 0x3c 0x9 0x0" #port 5 lane 0 + "0x08 0x00 0x3c 0xf 0x0" #port 5 lane 1 + "0x08 0x00 0x3c 0x9 0x0" #port 5 lane 2 + "0x08 0x00 0x3c 0x9 0x0" #port 5 lane 3 + "0x08 0x00 0x3c 0x9 0x0" #port 6 lane 0 + "0x08 0x00 0x3c 0x9 0x0" #port 6 lane 1 + "0x08 0x00 0x3c 0x9 0x0" #port 6 lane 2 + "0x08 0x00 0x3c 0xe 0x0" #port 6 lane 3 + "0x08 0x00 0x3c 0x9 0x0" #port 7 lane 0 + "0x08 0x00 0x3c 0xa 0x0" #port 7 lane 1 + "0x08 0x00 0x3c 0x9 0x0" #port 7 lane 2 + "0x08 0x00 0x3c 0xe 0x0" #port 7 lane 3 + "0x08 0x04 0x3c 0xf 0x0" #port 8 lane 0 + "0x08 0x00 0x3c 0xf 0x0" #port 8 lane 1 + "0x08 0x04 0x3c 0xf 0x0" #port 8 lane 2 + "0x08 0x00 0x3c 0xf 0x0" #port 8 lane 3 + "0x08 0x04 0x3c 0xf 0x0" #port 9 lane 0 + "0x08 0x04 0x3c 0xf 0x0" #port 9 lane 1 + "0x08 0x04 0x3c 0xf 0x0" #port 9 lane 2 + "0x08 0x04 0x3c 0xf 0x0" #port 9 lane 3 + "0x08 0x04 0x3c 0xf 0x0" #port 10 lane 0 + "0x10 0x08 0x3c 0xf 0x0" #port 10 lane 1 + "0x08 0x04 0x3c 0xf 0x0" #port 10 lane 2 + "0x08 0x04 0x3c 0xf 0x0" #port 10 lane 3 + "0x08 0x08 0x3c 0xf 0x0" #port 11 lane 0 + "0x08 0x04 0x3c 0xf 0x0" #port 11 lane 1 + "0x08 0x04 0x3c 0xf 0x0" #port 11 lane 2 + "0x08 0x04 0x3c 0xf 0x0" #port 11 lane 3 + "0x08 0x0c 0x3c 0xf 0x0" #port 12 lane 0 + "0x08 0x08 0x3c 0xf 0x0" #port 12 lane 1 + "0x08 0x0c 0x3c 0xf 0x0" #port 12 lane 2 + "0x08 0x08 0x3c 0xf 0x0" #port 12 lane 3 + "0x08 0x08 0x3c 0xf 0x0" #port 13 lane 0 + "0x08 0x0c 0x3c 0xf 0x0" #port 13 lane 1 + "0x08 0x08 0x3c 0xf 0x0" #port 13 lane 2 + "0x08 0x0c 0x3c 0xf 0x0" #port 13 lane 3 + "0x10 0x10 0x3c 0xf 0x0" #port 14 lane 0 + "0x08 0x0c 0x3c 0xf 0x0" #port 14 lane 1 + "0x08 0x0c 0x3c 0xf 0x0" #port 14 lane 2 + "0x08 0x0c 0x3c 0xf 0x0" #port 14 lane 3 + "0x08 0x14 0x3c 0xf 0x0" #port 15 lane 0 + "0x08 0x10 0x3c 0xf 0x0" #port 15 lane 1 + "0x08 0x10 0x3c 0xf 0x0" #port 15 lane 2 + "0x08 0x10 0x3c 0xf 0x0" #port 15 lane 3 +) + +#get field value in si value array for xe port +function get_xe_si { + local port=$1 + local field=$2 + local type=$3 + local index=$port + if [ "$type" == "${AOC_TYPE}" ]; then + SI_VAL=$(echo "${xe_aoc_si_value[$index]}" | awk '{print $'$field'}') + else + SI_VAL=$(echo "${xe_dac_si_value[$index]}" | awk '{print $'$field'}') + fi +} + +#get field value in si value array for ce port +function get_ce_si { + local port=$1 + local lane=$2 + local field=$3 + local type=$4 + index=$(( (${port}-1)*4+${lane} )) + if [ "$type" == "${AOC_TYPE}" ]; then + SI_VAL=$(echo "${ce_aoc_si_value[$index]}" | awk '{print $'$field'}') + else + SI_VAL=$(echo "${ce_dac_si_value[$index]}" | awk '{print $'$field'}') + fi +} + +# set si value for xe port +function _qsfp_xe_si_set { + # convert PORT to xe port index + local xe_port=$PORT + # generate command for SI PRE value + get_xe_si $xe_port $SI_PRE_IDX $TYPE + local pre_cmd="phy xe${xe_port} CL93N72_UT_CTL2r CL93N72_TXFIR_PRE=${SI_VAL}" + # generate command for SI POST value + get_xe_si $xe_port $SI_POST_IDX $TYPE + local post_cmd="phy xe${xe_port} CL93N72_UT_CTL2r CL93N72_TXFIR_POST=${SI_VAL}" + # generate command for SI MAIN value + get_xe_si $xe_port $SI_MAIN_IDX $TYPE + local main_cmd="phy xe${xe_port} CL93N72_UT_CTL3r CL93N72_TXFIR_MAIN=${SI_VAL}" + # generate command for SI AMP CONTROL value + get_xe_si $xe_port $SI_AMP_IDX $TYPE + local amp_val=$SI_VAL + get_xe_si $xe_port $SI_DRI_IDX $TYPE + local dri_val=$SI_VAL + local amp_cmd="phy xe${xe_port} AMS_TX_CTL2r AMS_TX_AMP_CTL=${amp_val} AMS_TX_DRIVERMODE=${dri_val}" + # apply bcmcmd + bcmcmd "${pre_cmd};${post_cmd};${main_cmd};${amp_cmd}" +} + +# set aoc si value for ce port +function _qsfp_ce_si_set { + # convert PORT to ce port index + local ce_port=$(( $PORT - 48 )) + for lane in {0..3}; + do + # generate command for SI PRE value + get_ce_si $ce_port $lane $SI_PRE_IDX $TYPE + local pre_cmd="phy ce${ce_port} CL93N72_UT_CTL2r.${lane} CL93N72_TXFIR_PRE=${SI_VAL}" + # generate command for SI POST value + get_ce_si $ce_port $lane $SI_POST_IDX $TYPE + local post_cmd="phy ce${ce_port} CL93N72_UT_CTL2r.${lane} CL93N72_TXFIR_POST=${SI_VAL}" + # generate command for SI MAIN value + get_ce_si $ce_port $lane $SI_MAIN_IDX $TYPE + local main_cmd="phy ce${ce_port} CL93N72_UT_CTL3r.${lane} CL93N72_TXFIR_MAIN=${SI_VAL}" + # generate command for SI AMP CONTROL value + get_ce_si $ce_port $lane $SI_AMP_IDX $TYPE + local amp_val=$SI_VAL + get_ce_si $ce_port $lane $SI_AMP_IDX $TYPE + local dri_val=$SI_VAL + local amp_cmd="phy ce${ce_port} AMS_TX_CTL2r.${lane} AMS_TX_AMP_CTL=${amp_val} AMS_TX_DRIVERMODE=${dri_val}" + # apply bcmcmd + bcmcmd "${pre_cmd};${post_cmd};${main_cmd};${amp_cmd}" + done +} + +#QSFP SI value set +function _qsfp_si_set { + if [[ $PORT -le 47 && $PORT -ge 0 ]]; then + # xe port + _qsfp_xe_si_set + else + # ce port + _qsfp_ce_si_set + fi +} + +function _util_input_check { + # input parameter validation + if [[ $1 -lt $2 || $1 -gt $3 ]]; then + echo "Please input number $2~$3" + exit + fi +} + +# Help usage function +function _help { + echo "=========================================================" + echo "# Description: Help Function" + echo "=========================================================" + echo "----------------------------------------------------" + echo "EX : ${0} help" + echo " : ${0} ${AOC_TYPE} [${MIN_PORT}-${MAX_PORT}]" + echo " : ${0} ${DAC_TYPE} [${MIN_PORT}-${MAX_PORT}]" + echo "----------------------------------------------------" +} + +#Main Function +function _main { + + # TODO: remove after SI value ready + #exit ${TRUE} + + if [ -z $PORT ]; then + _help + exit ${FALSE} + fi + + if [ "${TYPE}" == "help" ]; then + _help + elif [ "${TYPE}" == "${AOC_TYPE}" ] || [ "${TYPE}" == "${DAC_TYPE}" ]; then + _util_input_check "$PORT" "$MIN_PORT" "$MAX_PORT" + _qsfp_si_set + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi +} + +_main diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/utils/s8900_64xc_monitor.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/utils/s8900_64xc_monitor.sh new file mode 100755 index 000000000000..8bfb22328003 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s8900-64xc/utils/s8900_64xc_monitor.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=5 +I2C_UTILS="/usr/sbin/i2c_utils.sh" + + +# LED status monitor +function _led_monitor { + ${I2C_UTILS} i2c_led_fan_status_set >/dev/null + ${I2C_UTILS} i2c_led_psu_status_set >/dev/null + ${I2C_UTILS} i2c_led_fan_tray_status_set >/dev/null +} + +# main function +function _main { + while true + do + _led_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9100/README.md b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/README.md new file mode 100644 index 000000000000..78f828e25310 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/README.md @@ -0,0 +1,185 @@ +# Ingrasys S9100-32X Platform Driver for SONiC + +Copyright (C) 2016 Ingrasys, Inc. + +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 3 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, see . + + +## Licensing terms + +The Licensing terms of the files within this project is split 2 parts. +The Linux kernel is released under the GNU General Public License version 2. +All other code is released under the GNU General Public License version 3. +Please see the LICENSE file for copies of both licenses. + +## Contents of this package + + - service/ + > Service config files for platform initialization and monitoring + - utils/ + > Scripts useful during platform bringup and sysfs function + - conf/ + > Platform configure files. + +## Kernel modules and drivers + +The driver for interacting with Ingrasys S9100-32X is contained in the I2C +kernel module and initialization script. The initialization script loads +the modules in the correct order. It has been built and tested against +the Linux 3.16 kernel. + +The initialization script will modprobe the needed modules, navigate to the +module's device directory in sysfs, and write configuration data to +the kernel module. + +### IGB + +This is OOB Ports on front panel for management plane. + +The IGB module must be loaded first on Ingrasys S9100-32X platform. + +### I2C i801 + +The I2C i801 on Ingrasys S9100-32X can be found in +`/sys/bus/i2c/devices/i2c-0/` + +This is I2C bus for Clock Gen, DIMM channel and digital potentiometers. + +The i801 module must be loaded second on Ingrasys S9100-32X. + +### I2C iSMT + +The I2C iSMT module on S9100-32X can be found in +`/sys/bus/i2c/devices/i2c-1/` + +This is I2C bus for CPLD, HWM, power controller and I2C Switches. + +The i801 module must be loaded third on Ingrasys S9100-32X. + +### I2C PCA9548 +The PCA9548 module on S9100-32X can be found in +`/sys/bus/i2c/devices/i2c-2/` , `/sys/bus/i2c/devices/i2c-3/`, +`/sys/bus/i2c/devices/i2c-4/`, `/sys/bus/i2c/devices/i2c-5/`, +`/sys/bus/i2c/devices/i2c-6/`, `/sys/bus/i2c/devices/i2c-7/`, +`/sys/bus/i2c/devices/i2c-8/`, `/sys/bus/i2c/devices/i2c-9/`. + +The pca9548 module for zQSFP module get/set functions, PSU information, +fan status and EEPROM. + +## Hardware components + +The hardware components are initialized in the init script on S9100-32X. +The following describes manual initialization as well as interaction. +The examples below are just for Ingrasys S9100-32X platform. + +### Hardware initialization + +When the sonic-platform-ingrasys-s9100 package is installed on the S9100-32X, +it is automatically initialized. If you want to manual initialization, the +utility command usage as follows: +``` + i2c_utils.sh i2c_init +``` + +### EEPROM + +The EEPROM is including the board SKU, model name, vendor name, serial number, +and other information can be accessed with the specific eeprom kernel module. +After using `modprobe eeprom_mb` to detect the eeprom, it will show up in sysfs. + +The hexdump utility can be used to decode the raw output of the EEPROM. +For example, +``` + bash# echo "mb_eeprom 0x54" > /sys/bus/i2c/devices/i2c-9/new_device + bash# cat /sys/bus/i2c/drivers/mb_eeprom/9-0054/eeprom | hexdump -C +``` + +### Front panel LEDs + +LEDs can be setup on/off by using i2c utility `/usr/sbin/i2c_utils.sh`. +Utility function command usage as follows: + +``` +Status LED: + i2c_utils.sh i2c_sys_led green|amber on|off + +Fan status LED: + i2c_utils.sh i2c_fan_led green|amber on|off + +PSU1 status LED: + i2c_utils.sh i2c_psu1_led green|amber on|off + +PSU2 status LED: + i2c_utils.sh i2c_psu2_led green|amber on|off + +``` +QSFP Module Port LEDs control by ASIC library. + + +### Fan speed + +Fan speed are controlled by the w83795 kernel module. +It can be found in `/sys/class/hwmon/hwmon1/device/`. +If they were compiled as modules, you will need to modprobe w83795 for +their sysfs entries to show up. Each fan has an `fan_input` file +for reading the fan speed. And `pwm1` setting fan1 to fan4, +`pwm2` setting fan5 to fan8. + +There is docker-platform-monitor container installed fancontrol package that can +automatic control platform fan speed. + + +### Temperature sensors + +Temperature sensors are controlled by the w83795 kernel +module. It can be found in `/sys/class/hwmon/hwmon1/device/`. +If they were compiled as modules, then you will need to modprobe w83795 for +their sysfs entries to show up. +`temp1_input` is front MAC temperature sensor. `temp2_input` is rear MAC +temperature sensor. + +There is docker-platform-monitor container installed lm-sensors package that can +monitor platform temperature. And `sensors` command can show each +temperature sensors status. + +### Power supplies + +Power supplies status and its EEPROM info can be used i2c utility +`/usr/sbin/i2c_utils.sh` to get. +The usage as follows: +``` +PSU EEPROM: + i2c_utils.sh i2c_psu_eeprom_get + hexdump -C psu0.rom + hexdump -C psu1.rom + +PSU Status: + i2c_utils.sh i2c_psu_status +``` + +### QSFPs +QSFP modules are managed by the pca9548 kernel driver. +The i2c utility `/usr/sbin/i2c_utils.sh` can be used to get status and +module EEPROM informations. +The usage as follows: +``` +QSFP EEPROM: + i2c_utils.sh i2c_qsfp_eeprom_get [1-32] + +QSFP Insert Event: + i2c_utils.sh i2c_qsfp_eeprom_get [1-32] + 0 => not insert + 1 => inserted +``` + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9100/modules/Makefile b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/modules/Makefile new file mode 100755 index 000000000000..0247bd586f63 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/modules/Makefile @@ -0,0 +1,2 @@ +obj-m := eeprom_mb.o +obj-m+= i2c_cpld.o \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9100/modules/eeprom_mb.c b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/modules/eeprom_mb.c new file mode 100755 index 000000000000..01ce44ad3fee --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/modules/eeprom_mb.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 1998, 1999 Frodo Looijaard and + * Philip Edelbrock + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003 IBM Corp. + * Copyright (C) 2004 Jean Delvare + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, I2C_CLIENT_END }; + + +/* Size of EEPROM in bytes */ +#define EEPROM_SIZE 256 + +#define SLICE_BITS (6) +#define SLICE_SIZE (1 << SLICE_BITS) +#define SLICE_NUM (EEPROM_SIZE/SLICE_SIZE) + +/* Each client has this additional data */ +struct eeprom_data { + struct mutex update_lock; + u8 valid; /* bitfield, bit!=0 if slice is valid */ + unsigned long last_updated[SLICE_NUM]; /* In jiffies, 8 slices */ + u8 data[EEPROM_SIZE]; /* Register values */ +}; + + +static void mb_eeprom_update_client(struct i2c_client *client, u8 slice) +{ + struct eeprom_data *data = i2c_get_clientdata(client); + int i, j; + int ret; + int addr; + + mutex_lock(&data->update_lock); + + if (!(data->valid & (1 << slice)) || + time_after(jiffies, data->last_updated[slice] + 300 * HZ)) { + dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice); + + addr = slice << SLICE_BITS; + + ret = i2c_smbus_write_byte_data(client, ((u8)addr >> 8) & 0xFF, (u8)addr & 0xFF); + /* select the eeprom address */ + if (ret < 0) { + dev_err(&client->dev, "address set failed\n"); + goto exit; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE)) { + goto exit; + } + + for (i = slice << SLICE_BITS; i < (slice + 1) << SLICE_BITS; i+= SLICE_SIZE) { + for (j = i; j < (i+SLICE_SIZE); j++) { + int res; + + res = i2c_smbus_read_byte(client); + if (res < 0) { + goto exit; + } + + data->data[j] = res & 0xFF; + } + } + + data->last_updated[slice] = jiffies; + data->valid |= (1 << slice); + } +exit: + mutex_unlock(&data->update_lock); +} + +static ssize_t mb_eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct eeprom_data *data = i2c_get_clientdata(client); + u8 slice; + + if (off > EEPROM_SIZE) { + return 0; + } + if (off + count > EEPROM_SIZE) { + count = EEPROM_SIZE - off; + } + if (count == 0) { + return 0; + } + + /* Only refresh slices which contain requested bytes */ + for (slice = off >> SLICE_BITS; slice <= (off + count - 1) >> SLICE_BITS; slice++) { + mb_eeprom_update_client(client, slice); + } + + memcpy(buf, &data->data[off], count); + + return count; +} + +static struct bin_attribute mb_eeprom_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO, + }, + .size = EEPROM_SIZE, + .read = mb_eeprom_read, +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int mb_eeprom_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all + addresses 0x50-0x57, but we only care about 0x50. So decline + attaching to addresses >= 0x51 on DDC buses */ + if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x51) { + return -ENODEV; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE) + && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + return -ENODEV; + } + + strlcpy(info->type, "eeprom", I2C_NAME_SIZE); + + return 0; +} + +static int mb_eeprom_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct eeprom_data *data; + int err; + + if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + memset(data->data, 0xff, EEPROM_SIZE); + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &mb_eeprom_attr); + if (err) { + goto exit_kfree; + } + + return 0; + +exit_kfree: + kfree(data); +exit: + return err; +} + +static int mb_eeprom_remove(struct i2c_client *client) +{ + sysfs_remove_bin_file(&client->dev.kobj, &mb_eeprom_attr); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id mb_eeprom_id[] = { + { "mb_eeprom", 0 }, + { } +}; + +static struct i2c_driver mb_eeprom_driver = { + .driver = { + .name = "mb_eeprom", + }, + .probe = mb_eeprom_probe, + .remove = mb_eeprom_remove, + .id_table = mb_eeprom_id, + + .class = I2C_CLASS_DDC | I2C_CLASS_SPD, + .detect = mb_eeprom_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(mb_eeprom_driver); + +MODULE_AUTHOR("Wade "); +MODULE_DESCRIPTION("Ingrasys S9100 Mother Borad EEPROM driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9100/modules/i2c_cpld.c b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/modules/i2c_cpld.c new file mode 100755 index 000000000000..626c63177b50 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/modules/i2c_cpld.c @@ -0,0 +1,448 @@ +/* + * S9100-32X I2C CPLD driver + * + * Copyright (C) 2017 Ingrasys, Inc. + * Wade He + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "i2c_cpld.h" + +#ifdef DEBUG + #define DEBUG_PRINT(fmt, args...) \ + printk(KERN_INFO "%s[%d]: " fmt "\r\n", \ + __FUNCTION__, __LINE__, ##args) +#else + #define DEBUG_PRINT(fmt, args...) +#endif + +#define I2C_READ_BYTE_DATA(ret, lock, i2c_client, reg) \ +{ \ + mutex_lock(lock); \ + ret = i2c_smbus_read_byte_data(i2c_client, reg); \ + mutex_unlock(lock); \ +} +#define I2C_WRITE_BYTE_DATA(ret, lock, i2c_client, reg, val) \ +{ \ + mutex_lock(lock); \ + ret = i2c_smbus_write_byte_data(i2c_client, reg, val); \ + mutex_unlock(lock); \ +} + +/* CPLD sysfs attributes index */ +enum i2c_cpld_sysfs_attributes { + CPLD_ACCESS_REG, + CPLD_REGISTER_VAL, + CPLD_PORT_START, + CPLD_PORTS, + CPLD_VERSION, + CPLD_ID, + CPLD_BOARD_TYPE, + CPLD_EXT_BOARD_TYPE, + CPLD_PW_GOOD, + CPLD_PW_ABS, +}; + +/* CPLD sysfs attributes hook functions */ +static ssize_t read_access_register(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t write_access_register(struct device *dev, + struct device_attribute *da, const char *buf, size_t count); +static ssize_t read_register_value(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t write_register_value(struct device *dev, + struct device_attribute *da, const char *buf, size_t count); +static ssize_t read_cpld_version(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_board_type(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_ext_board_type(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_pw_good(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_pw_abs(struct device *dev, + struct device_attribute *da, char *buf); + +static LIST_HEAD(cpld_client_list); /* client list for cpld */ +static struct mutex list_lock; /* mutex for client list */ + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +struct cpld_data { + int index; /* CPLD index */ + struct mutex access_lock; /* mutex for cpld access */ + u8 access_reg; /* register to access */ +}; + +/* CPLD device id and data */ +static const struct i2c_device_id i2c_cpld_id[] = { + { "i2c_cpld", i2c_cpld }, + {} +}; + +/* Addresses scanned for i2c_cpld */ +static const unsigned short cpld_i2c_addr[] = { 0x33, I2C_CLIENT_END }; +/* platform sysfs object */ +extern struct kobject *s9230_64x_kobj; + + +/* define all support register access of cpld in attribute */ +static SENSOR_DEVICE_ATTR(cpld_access_register, S_IWUSR | S_IRUGO, + read_access_register, write_access_register, CPLD_ACCESS_REG); +static SENSOR_DEVICE_ATTR(cpld_register_value, S_IWUSR | S_IRUGO, + read_register_value, write_register_value, CPLD_REGISTER_VAL); +static SENSOR_DEVICE_ATTR(cpld_version, S_IRUGO, + read_cpld_version, NULL, CPLD_VERSION); +static SENSOR_DEVICE_ATTR(cpld_board_type, S_IRUGO, + read_board_type, NULL, CPLD_BOARD_TYPE); +static SENSOR_DEVICE_ATTR(cpld_ext_board_type, S_IRUGO, + read_ext_board_type, NULL, CPLD_EXT_BOARD_TYPE); +static SENSOR_DEVICE_ATTR(cpld_pw_good, S_IRUGO, + read_pw_good, NULL, CPLD_PW_GOOD); +static SENSOR_DEVICE_ATTR(cpld_pw_abs, S_IRUGO, + read_pw_abs, NULL, CPLD_PW_ABS); + + +/* define support attributes of cpldx , total 5 */ +/* cpld 1 */ +static struct attribute *i2c_cpld_attributes[] = { + &sensor_dev_attr_cpld_access_register.dev_attr.attr, + &sensor_dev_attr_cpld_register_value.dev_attr.attr, + &sensor_dev_attr_cpld_version.dev_attr.attr, + &sensor_dev_attr_cpld_board_type.dev_attr.attr, + &sensor_dev_attr_cpld_ext_board_type.dev_attr.attr, + &sensor_dev_attr_cpld_pw_good.dev_attr.attr, + &sensor_dev_attr_cpld_pw_abs.dev_attr.attr, + NULL +}; + +/* cpld 1 attributes group */ +static const struct attribute_group i2c_cpld_group = { + .attrs = i2c_cpld_attributes, +}; + +/* read access register from cpld data */ +static ssize_t read_access_register(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg = data->access_reg; + + return sprintf(buf, "0x%x\n", reg); +} + +/* write access register to cpld data */ +static ssize_t write_access_register(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + + if (kstrtou8(buf, 0, ®) < 0) + return -EINVAL; + + data->access_reg = reg; + return count; +} + +/* read the value of access register in cpld data */ +static ssize_t read_register_value(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg = data->access_reg; + int reg_val; + + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + + if (reg_val < 0) + return -1; + + return sprintf(buf, "0x%x\n", reg_val); +} + +/* wrtie the value to access register in cpld data */ +static ssize_t write_register_value(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + int ret = -EIO; + u8 reg = data->access_reg; + u8 reg_val; + + if (kstrtou8(buf, 0, ®_val) < 0) + return -EINVAL; + + I2C_WRITE_BYTE_DATA(ret, &data->access_lock, client, reg, reg_val); + + return count; +} + +/* get cpdl version register value */ +static ssize_t read_cpld_version(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_VERSION) { + reg = CPLD_VERSION_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get board type register value */ +static ssize_t read_board_type(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_BOARD_TYPE) { + reg = CPLD_BOARD_TYPE_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get extend board type register value */ +static ssize_t read_ext_board_type(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_EXT_BOARD_TYPE) { + reg = CPLD_EXT_BOARD_TYPE_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get extend board type register value */ +static ssize_t read_pw_good(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_PW_GOOD) { + reg = CPLD_PW_GOOD_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get extend board type register value */ +static ssize_t read_pw_abs(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_PW_ABS) { + reg = CPLD_PW_ABS_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* add valid cpld client to list */ +static void i2c_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = NULL; + + node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + if (!node) { + dev_info(&client->dev, + "Can't allocate cpld_client_node for index %d\n", + client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +/* remove exist cpld client in list */ +static void i2c_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + list_for_each(list_node, &cpld_client_list) { + cpld_node = list_entry(list_node, + struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + mutex_unlock(&list_lock); +} + +/* cpld drvier probe */ +static int i2c_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + struct cpld_data *data = NULL; + + data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* init cpld data for client */ + i2c_set_clientdata(client, data); + mutex_init(&data->access_lock); + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_info(&client->dev, + "i2c_check_functionality failed (0x%x)\n", + client->addr); + status = -EIO; + goto exit; + } + + + status = sysfs_create_group(&client->dev.kobj,&i2c_cpld_group); + + if (status) + goto exit; + + dev_info(&client->dev, "chip found\n"); + + /* add probe chip to client list */ + i2c_cpld_add_client(client); + + return 0; +exit: + sysfs_remove_group(&client->dev.kobj, &i2c_cpld_group); + return status; +} + +/* cpld drvier remove */ +static int i2c_cpld_remove(struct i2c_client *client) +{ + + sysfs_remove_group(&client->dev.kobj, &i2c_cpld_group); + + i2c_cpld_remove_client(client); + return 0; +} + +MODULE_DEVICE_TABLE(i2c, i2c_cpld_id); + +static struct i2c_driver i2c_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "i2c_cpld", + }, + .probe = i2c_cpld_probe, + .remove = i2c_cpld_remove, + .id_table = i2c_cpld_id, + .address_list = cpld_i2c_addr, +}; + +static int __init i2c_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&i2c_cpld_driver); +} + +static void __exit i2c_cpld_exit(void) +{ + i2c_del_driver(&i2c_cpld_driver); +} + +MODULE_AUTHOR("Wade He "); +MODULE_DESCRIPTION("Ingrasys S9100-32X Platform i2c cpld driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_cpld_init); +module_exit(i2c_cpld_exit); + + + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9100/modules/i2c_cpld.h b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/modules/i2c_cpld.h new file mode 100755 index 000000000000..e103b05c717a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/modules/i2c_cpld.h @@ -0,0 +1,197 @@ +/* + * + * S9100-32X I2C CPLD driver header file + * + * Copyright (C) 2017 Ingrasys, Inc. + * Wade He + * + * 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, see . + */ + +#ifndef I2C_CPLD_H +#define I2C_CPLD_H + +// remove debug before release +#define DEBUG + +/* CPLD device index value */ +enum cpld_id { + i2c_cpld +}; + +/* port number on CPLD */ +#define CPLD_1_PORT_NUM 12 +#define CPLD_2_PORT_NUM 13 + +/* QSFP port number */ +#define QSFP_MAX_PORT_NUM 64 +#define QSFP_MIN_PORT_NUM 1 + +/* SFP+ port number */ +#define SFP_MAX_PORT_NUM 2 +#define SFP_MIN_PORT_NUM 1 + + +/* CPLD registers */ +#define CPLD_BOARD_TYPE_REG 0x0 +#define CPLD_EXT_BOARD_TYPE_REG 0x7 +#define CPLD_VERSION_REG 0x1 +#define CPLD_PW_GOOD_REG 0x2 +#define CPLD_PW_ABS_REG 0x3 + + +/* bit definition for register value */ + +enum CPLD_RESET_CONTROL_BITS { + CPLD_RESET_CONTROL_SWRST_BIT, + CPLD_RESET_CONTROL_CP2104RST_BIT, + CPLD_RESET_CONTROL_82P33814RST_BIT, + CPLD_RESET_CONTROL_BMCRST_BIT, +}; + +/* bit field structure for register value */ +struct cpld_reg_board_type_t { + u8 build_rev:2; + u8 hw_rev:2; + u8 board_id:4; +}; + +struct cpld_reg_version_t { + u8 revision:6; + u8 release:1; + u8 reserve:1; +}; + +struct cpld_reg_pw_good_t { + u8 reserve1:3; + u8 psu1:1; + u8 psu2:1; + u8 reserve2:3; +}; + +struct cpld_reg_pw_abs_t { + u8 psu1:1; + u8 psu2:1; + u8 reserve:6; +}; + +/* common manipulation */ +#define INVALID(i, min, max) ((i < min) || (i > max) ? 1u : 0u) +#define READ_BIT(val, bit) ((0u == (val & (1<bf_name) +#define READ_BF_1(bf_struct, val, bf_name, bf_value) \ + bf_struct bf; \ + bf.data = val; \ + bf_value = bf.bf_name +#define BOARD_TYPE_BUILD_REV_GET(val, res) \ + READ_BF(cpld_reg_board_type_t, val, build_rev, res) +#define BOARD_TYPE_HW_REV_GET(val, res) \ + READ_BF(cpld_reg_board_type_t, val, hw_rev, res) +#define BOARD_TYPE_BOARD_ID_GET(val, res) \ + READ_BF(cpld_reg_board_type_t, val, board_id, res) +#define CPLD_VERSION_REV_GET(val, res) \ + READ_BF(cpld_reg_version_t, val, revision, res) +#define CPLD_VERSION_REL_GET(val, res) \ + READ_BF(cpld_reg_version_t, val, release, res) +#define CPLD_PSU1_PW_GOOD_GET(val, res) \ + READ_BF(cpld_reg_pw_good_t, val, psu1, res) +#define CPLD_PSU2_PW_GOOD_GET(val, res) \ + READ_BF(cpld_reg_pw_good_t, val, psu2, res) +#define CPLD_PSU1_PW_ABS_GET(val, res) \ + READ_BF(cpld_reg_pw_abs_t, val, psu1, res) +#define CPLD_PSU2_PW_ABS_GET(val, res) \ + READ_BF(cpld_reg_pw_abs_t, val, psu2, res) +/* QSFP/SFP registers manipulation */ +#define QSFP_TO_CPLD_IDX(qsfp_port, cpld_index, cpld_port) \ +{ \ + if (QSFP_MIN_PORT_NUM <= qsfp_port && qsfp_port <= CPLD_1_PORT_NUM) { \ + cpld_index = cpld1; \ + cpld_port = qsfp_port - 1; \ + } else if (CPLD_1_PORT_NUM < qsfp_port \ + && qsfp_port <= QSFP_MAX_PORT_NUM) { \ + cpld_index = cpld2 + (qsfp_port - 1 - CPLD_1_PORT_NUM) \ + / CPLD_2_PORT_NUM; \ + cpld_port = (qsfp_port - 1 - CPLD_1_PORT_NUM) % \ + CPLD_2_PORT_NUM; \ + } else { \ + cpld_index = 0; \ + cpld_port = 0; \ + } \ +} +#define SFP_TO_CPLD_IDX(sfp_port, cpld_index) \ + (cpld_index = sfp_port - SFP_MIN_PORT_NUM) +#define QSFP_PORT_STATUS_REG(cpld_port) \ + (CPLD_QSFP_PORT_STATUS_BASE_REG + cpld_port) +#define QSFP_PORT_CONFIG_REG(cpld_port) \ + (CPLD_QSFP_PORT_CONFIG_BASE_REG + cpld_port) +#define QSFP_PORT_INT_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_QSFP_PORT_STATUS_INT_BIT) +#define QSFP_PORT_ABS_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_QSFP_PORT_STATUS_ABS_BIT) +#define QSFP_PORT_RESET_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_RESET_BIT) +#define QSFP_PORT_LPMODE_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_LPMODE_BIT) +#define QSFP_PORT_RESET_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_RESET_BIT) +#define QSFP_PORT_RESET_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_RESET_BIT) +#define QSFP_PORT_LPMODE_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_LPMODE_BIT) +#define QSFP_PORT_LPMODE_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_LPMODE_BIT) +#define SFP_PORT_PRESENT_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_STATUS_PRESENT_BIT) + + #define SFP_PORT_TXFAULT_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_STATUS_TXFAULT_BIT) + #define SFP_PORT_RXLOS_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_STATUS_RXLOS_BIT) + #define SFP_PORT_TXDIS_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_CONFIG_TXDIS_BIT) +#define SFP_PORT_RS_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_RS_BIT) +#define SFP_PORT_TS_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TS_BIT) +#define SFP_PORT_TXDIS_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TXDIS_BIT) +#define SFP_PORT_TXDIS_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TXDIS_BIT) +#define SFP_PORT_RS_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_RS_BIT) +#define SFP_PORT_RS_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_RS_BIT) +#define SFP_PORT_TS_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TS_BIT) +#define SFP_PORT_TS_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TS_BIT) + +/* CPLD access functions */ +extern int i2c_cpld_get_qsfp_port_status_val(u8 port_num); +extern int i2c_cpld_get_qsfp_port_config_val(u8 port_num); +extern int i2c_cpld_set_qsfp_port_config_val(u8 port_num, u8 reg_val); +extern int i2c_cpld_get_sfp_port_status_val(u8 port_num); +extern int i2c_cpld_get_sfp_port_config_val(u8 port_num); +extern int i2c_cpld_set_sfp_port_config_val(u8 port_num, u8 reg_val); +extern u8 fp_port_to_phy_port(u8 fp_port); +#endif + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9100/service/qsfp-monitor.service b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/service/qsfp-monitor.service new file mode 100644 index 000000000000..584d6fcae895 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/service/qsfp-monitor.service @@ -0,0 +1,15 @@ +[Unit] +Description= This QSFP Monitor service is to setup QSFP SI. +Requires=s9100-monitor.service +After=s9100-monitor.service + +[Service] +ExecStart=/usr/sbin/qsfp_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9100/service/s9100-monitor.service b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/service/s9100-monitor.service new file mode 100644 index 000000000000..5e074782a852 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/service/s9100-monitor.service @@ -0,0 +1,19 @@ +[Unit] +Description= This Platform Monitor service is to initialize platform and monitor platform. +Before=platform-monitor.service +After=sysinit.target +Wants=fancontrol.service +Wants=qsfp-monitor.service +DefaultDependencies=no + +[Service] +ExecStartPre=/usr/sbin/i2c_utils.sh i2c_init +ExecStart=/usr/sbin/s9100_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9100/utils/i2c_utils.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/utils/i2c_utils.sh new file mode 100755 index 000000000000..bdcd4f1d7f9e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/utils/i2c_utils.sh @@ -0,0 +1,1518 @@ +#!/bin/bash + +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +VERSION="1.0.0" +TRUE=200 +FALSE=404 + +EXEC_FUNC=${1} +COLOR_LED=${2} +QSFP_PORT=${2} +QSFP_ACTION=${2} +MB_EEPROM_ACTION=${2} +ONOFF_LED=${3} +FAN_TRAY=${4} + +############################################################ +# Distributor ID: Debian +# Description: Debian GNU/Linux 8.6 (jessie) +# Release: 8.6 +# Codename: jessie +# Linux debian 3.16.0-4-amd64 #1 +# SMP Debian 3.16.36-1+deb8u1 (2016-09-03) x86_64 GNU/Linux +############################################################ + +# Color Definition +COLOR_TITLE="\e[1;32m" ### Green ### +COLOR_WARNING="\e[1;33m" ### Yellow ### +COLOR_ERROR="\e[1;31m" ### Red ### +COLOR_END="\e[0m" ### END ### + +NUM_IGB_DEVICE=0 +NUM_I801_DEVICE=0 +NUM_ISMT_DEVICE=$(( ${NUM_I801_DEVICE} + 1 )) +NUM_MUX1_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 2 )) +NUM_MUX1_CHAN1_DEVICE=$(( ${NUM_I801_DEVICE} + 3 )) +NUM_MUX1_CHAN2_DEVICE=$(( ${NUM_I801_DEVICE} + 4 )) +NUM_MUX1_CHAN3_DEVICE=$(( ${NUM_I801_DEVICE} + 5 )) +NUM_MUX1_CHAN4_DEVICE=$(( ${NUM_I801_DEVICE} + 6 )) +NUM_MUX1_CHAN5_DEVICE=$(( ${NUM_I801_DEVICE} + 7 )) +NUM_MUX1_CHAN6_DEVICE=$(( ${NUM_I801_DEVICE} + 8 )) +NUM_MUX1_CHAN7_DEVICE=$(( ${NUM_I801_DEVICE} + 9 )) +NUM_MUX3_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 10 )) +NUM_MUX4_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 18 )) +NUM_MUX5_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 26 )) +NUM_MUX6_CHAN0_DEVICE=$(( ${NUM_I801_DEVICE} + 34 )) + +PATH_SYS_I2C_DEVICES="/sys/bus/i2c/devices" +PATH_HWMON_ROOT_DEVICES="/sys/class/hwmon" +PATH_HWMON_W83795_DEVICE="${PATH_HWMON_ROOT_DEVICES}/hwmon1" +PATH_I801_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_I801_DEVICE}" +PATH_ISMT_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_ISMT_DEVICE}" +PATH_MUX_CHAN0_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN0_DEVICE}" +PATH_MUX_CHAN1_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN1_DEVICE}" +PATH_MUX_CHAN2_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN2_DEVICE}" +PATH_MUX_CHAN3_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN3_DEVICE}" +PATH_MUX_CHAN4_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN4_DEVICE}" +PATH_MUX_CHAN5_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN5_DEVICE}" +PATH_MUX_CHAN6_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN6_DEVICE}" +PATH_MUX_CHAN7_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN7_DEVICE}" + +#Power Supply Status +PSU_DC_ON=1 +PSU_DC_OFF=0 +PSU_EXIST=1 +PSU_NOT_EXIST=0 + +# Help usage function +function _help { + echo "=========================================================" + echo "# Description: Help Function" + echo "=========================================================" + echo "----------------------------------------------------" + echo "EX : ${0} help" + echo " : ${0} i2c_init" + echo " : ${0} i2c_deinit" + echo " : ${0} i2c_temp_init" + echo " : ${0} i2c_fan_init" + echo " : ${0} i2c_volmon_init" + echo " : ${0} i2c_io_exp_init" + echo " : ${0} i2c_gpio_init" + echo " : ${0} i2c_gpio_deinit" + echo " : ${0} i2c_led_test" + echo " : ${0} i2c_psu_eeprom_get" + echo " : ${0} i2c_mb_eeprom_get" + echo " : ${0} i2c_qsfp_eeprom_get [1-32]" + echo " : ${0} i2c_qsfp_eeprom_init new|delete" + echo " : ${0} i2c_mb_eeprom_init new|delete" + echo " : ${0} i2c_qsfp_status_get [1-32]" + echo " : ${0} i2c_qsfp_type_get [1-32]" + echo " : ${0} i2c_board_type_get" + echo " : ${0} i2c_psu_status" + echo " : ${0} i2c_led_psu_status_set" + echo " : ${0} i2c_led_fan_status_set" + echo " : ${0} i2c_led_fan_tray_status_set" + echo " : ${0} i2c_cpld_version" + echo " : ${0} i2c_front_temp" + echo " : ${0} i2c_rear_temp" + echo " : ${0} i2c_test_all" + echo " : ${0} i2c_sys_led green|amber on|off" + echo " : ${0} i2c_fan_led green|amber on|off" + echo " : ${0} i2c_psu1_led green|amber on|off" + echo " : ${0} i2c_psu2_led green|amber on|off" + echo " : ${0} i2c_fan_tray_led green|amber on|off [1-4]" + echo "----------------------------------------------------" +} + +#Pause function +function _pause { + read -p "$*" +} + +#Retry command function +function _retry { + local i + for i in {1..5}; + do + eval "${*}" && break || echo "retry"; sleep 1; + done +} + +#I2C Init +function _i2c_init { + echo "=========================================================" + echo "# Description: I2C Init" + echo "=========================================================" + + rmmod i2c_ismt + rmmod i2c_i801 + modprobe i2c_i801 + modprobe i2c_ismt + modprobe i2c_dev + modprobe i2c_mux_pca954x force_deselect_on_exit=1 + + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x70' > ${PATH_ISMT_DEVICE}/new_device" + else + echo "pca9548 0x70 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX3_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x71' > ${PATH_MUX_CHAN0_DEVICE}/new_device" + else + echo "pca9548 0x71 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX4_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x72' > ${PATH_MUX_CHAN1_DEVICE}/new_device" + else + echo "pca9548 0x72 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX5_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x73' > ${PATH_MUX_CHAN2_DEVICE}/new_device" + else + echo "pca9548 0x73 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX6_CHAN0_DEVICE}" ]; then + _retry "echo 'pca9548 0x74' > ${PATH_MUX_CHAN3_DEVICE}/new_device" + else + echo "pca9548 0x74 already init." + fi + if [ ! -e "${PATH_SYS_I2C_DEVICES}/${NUM_MUX1_CHAN6_DEVICE}-0075" ]; then + _retry "echo 'pca9546 0x75' > ${PATH_MUX_CHAN6_DEVICE}/new_device" + else + echo "pca9548 0x75 already init." + fi + #Init CPLD LED_CLR Register (Front Port LED) + i2cset -y ${NUM_I801_DEVICE} 0x33 0x34 0x10 + + rmmod coretemp + rmmod jc42 + rmmod w83795 + _i2c_temp_init + _i2c_volmon_init + modprobe coretemp + modprobe w83795 + modprobe jc42 + modprobe eeprom + modprobe eeprom_mb + modprobe gpio-pca953x + _i2c_fan_init + _i2c_io_exp_init + _i2c_gpio_init + _i2c_cpld_init + _i2c_qsfp_eeprom_init "new" + _i2c_mb_eeprom_init "new" + _i2c_psu_eeprom_init + _i2c_led_psu_status_set + _i2c_led_fan_status_set + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_sys_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_sys_led + _config_rmem +} + +#I2C Deinit +function _i2c_deinit { + _i2c_gpio_deinit + for mod in i2c_cpld coretemp jc42 w83795 eeprom_mb gpio-pca953x i2c_mux_pca954x i2c_ismt i2c_i801; + do + [ "$(lsmod | grep "^$mod ")" != "" ] && rmmod $mod + done +} + +#Temperature sensor Init +function _i2c_temp_init { + echo -n "TEMP INIT..." + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x00 0x80 + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x05 0x7F + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x04 0x0A + echo "Done" +} + +#FAN Init +function _i2c_fan_init { + echo -n "FAN INIT..." + if [ -e "${PATH_HWMON_W83795_DEVICE}" ]; then + echo 120 > ${PATH_HWMON_W83795_DEVICE}/device/pwm1 + echo 120 > ${PATH_HWMON_W83795_DEVICE}/device/pwm2 + echo "SUCCESS" + else + echo "FAIL" + fi + +} + +#VOLMON Init +function _i2c_volmon_init { + echo -n "VOLMON INIT..." + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x00 0x80 + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x02 0xFF + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x03 0x50 + i2cset -y -r ${NUM_I801_DEVICE} 0x2F 0x04 0x0A + echo "Done" +} + +#IO Expander Init +function _i2c_io_exp_init { + echo "=========================================================" + echo "# Description: I2C IO Expender Init" + echo "=========================================================" + #SMBUS0 IO_EXPENDER + i2cset -y -r ${NUM_I801_DEVICE} 0x27 4 0x00 + i2cset -y -r ${NUM_I801_DEVICE} 0x27 5 0x00 + i2cset -y -r ${NUM_I801_DEVICE} 0x27 2 0x00 + i2cset -y -r ${NUM_I801_DEVICE} 0x27 3 0x00 + i2cset -y -r ${NUM_I801_DEVICE} 0x27 6 0xFF + i2cset -y -r ${NUM_I801_DEVICE} 0x27 7 0xFF + + #SMBUS1 + #ABS + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x20 6 0xFF + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x20 7 0xFF + + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x21 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x21 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x21 6 0xFF + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x21 7 0xFF + + #Transcevior INT + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 6 0xFF + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x22 7 0xFF + + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x23 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x23 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x23 6 0xFF + i2cset -y -r ${NUM_MUX1_CHAN4_DEVICE} 0x23 7 0xFF + + echo "Init ZQSFP IO Expender" + echo "set ZQSFP LP_MODE = 0" + #set ZQSFP LP_MODE = 0 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x20 2 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x20 3 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x20 6 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x20 7 0x00 + + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x21 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x21 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x21 2 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x21 3 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x21 6 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x21 7 0x00 + + echo "set ZQSFP RST = 1" + #set ZQSFP RST = 1 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x22 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x22 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x22 2 0xFF + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x22 3 0xFF + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x22 6 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x22 7 0x00 + + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x23 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x23 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x23 2 0xFF + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x23 3 0xFF + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x23 6 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x23 7 0x00 + + echo "set ZQSFP mode" + #ZQSFP mode + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x24 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x24 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x24 2 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x24 3 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x24 6 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x24 7 0x00 + + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x25 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x25 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x25 2 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x25 3 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x25 6 0x00 + i2cset -y -r ${NUM_MUX1_CHAN5_DEVICE} 0x25 7 0x00 + + #ZQSFP/SFP+/E-Card General + i2cset -y -r ${NUM_MUX1_CHAN6_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN6_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN6_DEVICE} 0x20 6 0xFF + i2cset -y -r ${NUM_MUX1_CHAN6_DEVICE} 0x20 7 0xFF + + #LED board after PVT (S9100_IO_EXP_LED_ID) + echo "Init LED IO Expender" + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 6 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 7 0x00 + + #PSU I/O (S9100_IO_EXP_PSU_ID) + echo "Init PSU IO Expender" + i2cset -y -r ${NUM_MUX1_CHAN6_DEVICE} 0x23 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN6_DEVICE} 0x23 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN6_DEVICE} 0x23 2 0x00 + i2cset -y -r ${NUM_MUX1_CHAN6_DEVICE} 0x23 3 0x00 + i2cset -y -r ${NUM_MUX1_CHAN6_DEVICE} 0x23 6 0xBB + i2cset -y -r ${NUM_MUX1_CHAN6_DEVICE} 0x23 7 0xFF + + #FAN I/O (S9100_IO_EXP_FAN_ID) + echo "Init FAN IO Expender" + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x20 4 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x20 5 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x20 2 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x20 3 0x00 + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x20 6 0xCC + i2cset -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x20 7 0xCC +} + +#GPIO Init +function _i2c_gpio_init { + #ABS Port 0-15 + echo "pca9535 0x20" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN4_DEVICE}/new_device + echo 240 > /sys/class/gpio/export + echo 241 > /sys/class/gpio/export + echo 242 > /sys/class/gpio/export + echo 243 > /sys/class/gpio/export + echo 244 > /sys/class/gpio/export + echo 245 > /sys/class/gpio/export + echo 246 > /sys/class/gpio/export + echo 247 > /sys/class/gpio/export + echo 248 > /sys/class/gpio/export + echo 249 > /sys/class/gpio/export + echo 250 > /sys/class/gpio/export + echo 251 > /sys/class/gpio/export + echo 252 > /sys/class/gpio/export + echo 253 > /sys/class/gpio/export + echo 254 > /sys/class/gpio/export + echo 255 > /sys/class/gpio/export + echo 1 > /sys/class/gpio/gpio241/active_low #zQSFP00 + echo 1 > /sys/class/gpio/gpio240/active_low #zQSFP01 + echo 1 > /sys/class/gpio/gpio243/active_low #zQSFP02 + echo 1 > /sys/class/gpio/gpio242/active_low #zQSFP03 + echo 1 > /sys/class/gpio/gpio245/active_low #zQSFP04 + echo 1 > /sys/class/gpio/gpio244/active_low #zQSFP05 + echo 1 > /sys/class/gpio/gpio247/active_low #zQSFP06 + echo 1 > /sys/class/gpio/gpio246/active_low #zQSFP07 + echo 1 > /sys/class/gpio/gpio249/active_low #zQSFP08 + echo 1 > /sys/class/gpio/gpio248/active_low #zQSFP09 + echo 1 > /sys/class/gpio/gpio251/active_low #zQSFP10 + echo 1 > /sys/class/gpio/gpio250/active_low #zQSFP11 + echo 1 > /sys/class/gpio/gpio253/active_low #zQSFP12 + echo 1 > /sys/class/gpio/gpio252/active_low #zQSFP13 + echo 1 > /sys/class/gpio/gpio255/active_low #zQSFP14 + echo 1 > /sys/class/gpio/gpio254/active_low #zQSFP15 + + #ABS Port 16-31 + echo "pca9535 0x21" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN4_DEVICE}/new_device + echo 224 > /sys/class/gpio/export + echo 225 > /sys/class/gpio/export + echo 226 > /sys/class/gpio/export + echo 227 > /sys/class/gpio/export + echo 228 > /sys/class/gpio/export + echo 229 > /sys/class/gpio/export + echo 230 > /sys/class/gpio/export + echo 231 > /sys/class/gpio/export + echo 232 > /sys/class/gpio/export + echo 233 > /sys/class/gpio/export + echo 234 > /sys/class/gpio/export + echo 235 > /sys/class/gpio/export + echo 236 > /sys/class/gpio/export + echo 237 > /sys/class/gpio/export + echo 238 > /sys/class/gpio/export + echo 239 > /sys/class/gpio/export + echo 1 > /sys/class/gpio/gpio225/active_low #zQSFP16 + echo 1 > /sys/class/gpio/gpio224/active_low #zQSFP17 + echo 1 > /sys/class/gpio/gpio227/active_low #zQSFP18 + echo 1 > /sys/class/gpio/gpio226/active_low #zQSFP19 + echo 1 > /sys/class/gpio/gpio229/active_low #zQSFP20 + echo 1 > /sys/class/gpio/gpio228/active_low #zQSFP21 + echo 1 > /sys/class/gpio/gpio231/active_low #zQSFP22 + echo 1 > /sys/class/gpio/gpio230/active_low #zQSFP23 + echo 1 > /sys/class/gpio/gpio233/active_low #zQSFP24 + echo 1 > /sys/class/gpio/gpio232/active_low #zQSFP25 + echo 1 > /sys/class/gpio/gpio235/active_low #zQSFP26 + echo 1 > /sys/class/gpio/gpio234/active_low #zQSFP27 + echo 1 > /sys/class/gpio/gpio237/active_low #zQSFP28 + echo 1 > /sys/class/gpio/gpio236/active_low #zQSFP29 + echo 1 > /sys/class/gpio/gpio239/active_low #zQSFP30 + echo 1 > /sys/class/gpio/gpio238/active_low #zQSFP31 + + #INT Port 0-15 + echo "pca9535 0x22" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN4_DEVICE}/new_device + echo 208 > /sys/class/gpio/export + echo 209 > /sys/class/gpio/export + echo 210 > /sys/class/gpio/export + echo 211 > /sys/class/gpio/export + echo 212 > /sys/class/gpio/export + echo 213 > /sys/class/gpio/export + echo 214 > /sys/class/gpio/export + echo 215 > /sys/class/gpio/export + echo 216 > /sys/class/gpio/export + echo 217 > /sys/class/gpio/export + echo 218 > /sys/class/gpio/export + echo 219 > /sys/class/gpio/export + echo 220 > /sys/class/gpio/export + echo 221 > /sys/class/gpio/export + echo 222 > /sys/class/gpio/export + echo 223 > /sys/class/gpio/export + echo 1 > /sys/class/gpio/gpio208/active_low + echo 1 > /sys/class/gpio/gpio209/active_low + echo 1 > /sys/class/gpio/gpio210/active_low + echo 1 > /sys/class/gpio/gpio211/active_low + echo 1 > /sys/class/gpio/gpio212/active_low + echo 1 > /sys/class/gpio/gpio213/active_low + echo 1 > /sys/class/gpio/gpio214/active_low + echo 1 > /sys/class/gpio/gpio215/active_low + echo 1 > /sys/class/gpio/gpio216/active_low + echo 1 > /sys/class/gpio/gpio217/active_low + echo 1 > /sys/class/gpio/gpio218/active_low + echo 1 > /sys/class/gpio/gpio219/active_low + echo 1 > /sys/class/gpio/gpio220/active_low + echo 1 > /sys/class/gpio/gpio221/active_low + echo 1 > /sys/class/gpio/gpio222/active_low + echo 1 > /sys/class/gpio/gpio223/active_low + + #INT Port 16-31 + echo "pca9535 0x23" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN4_DEVICE}/new_device + echo 192 > /sys/class/gpio/export + echo 193 > /sys/class/gpio/export + echo 194 > /sys/class/gpio/export + echo 195 > /sys/class/gpio/export + echo 196 > /sys/class/gpio/export + echo 197 > /sys/class/gpio/export + echo 198 > /sys/class/gpio/export + echo 199 > /sys/class/gpio/export + echo 200 > /sys/class/gpio/export + echo 201 > /sys/class/gpio/export + echo 202 > /sys/class/gpio/export + echo 203 > /sys/class/gpio/export + echo 204 > /sys/class/gpio/export + echo 205 > /sys/class/gpio/export + echo 206 > /sys/class/gpio/export + echo 207 > /sys/class/gpio/export + echo 1 > /sys/class/gpio/gpio192/active_low + echo 1 > /sys/class/gpio/gpio193/active_low + echo 1 > /sys/class/gpio/gpio194/active_low + echo 1 > /sys/class/gpio/gpio195/active_low + echo 1 > /sys/class/gpio/gpio196/active_low + echo 1 > /sys/class/gpio/gpio197/active_low + echo 1 > /sys/class/gpio/gpio198/active_low + echo 1 > /sys/class/gpio/gpio199/active_low + echo 1 > /sys/class/gpio/gpio200/active_low + echo 1 > /sys/class/gpio/gpio201/active_low + echo 1 > /sys/class/gpio/gpio202/active_low + echo 1 > /sys/class/gpio/gpio203/active_low + echo 1 > /sys/class/gpio/gpio204/active_low + echo 1 > /sys/class/gpio/gpio205/active_low + echo 1 > /sys/class/gpio/gpio206/active_low + echo 1 > /sys/class/gpio/gpio207/active_low + + #LP Mode Port 0-15 + echo "pca9535 0x20" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN5_DEVICE}/new_device + echo 176 > /sys/class/gpio/export + echo 177 > /sys/class/gpio/export + echo 178 > /sys/class/gpio/export + echo 179 > /sys/class/gpio/export + echo 180 > /sys/class/gpio/export + echo 181 > /sys/class/gpio/export + echo 182 > /sys/class/gpio/export + echo 183 > /sys/class/gpio/export + echo 184 > /sys/class/gpio/export + echo 185 > /sys/class/gpio/export + echo 186 > /sys/class/gpio/export + echo 187 > /sys/class/gpio/export + echo 188 > /sys/class/gpio/export + echo 189 > /sys/class/gpio/export + echo 190 > /sys/class/gpio/export + echo 191 > /sys/class/gpio/export + echo out > /sys/class/gpio/gpio176/direction + echo out > /sys/class/gpio/gpio177/direction + echo out > /sys/class/gpio/gpio178/direction + echo out > /sys/class/gpio/gpio179/direction + echo out > /sys/class/gpio/gpio180/direction + echo out > /sys/class/gpio/gpio181/direction + echo out > /sys/class/gpio/gpio182/direction + echo out > /sys/class/gpio/gpio183/direction + echo out > /sys/class/gpio/gpio184/direction + echo out > /sys/class/gpio/gpio185/direction + echo out > /sys/class/gpio/gpio186/direction + echo out > /sys/class/gpio/gpio187/direction + echo out > /sys/class/gpio/gpio188/direction + echo out > /sys/class/gpio/gpio189/direction + echo out > /sys/class/gpio/gpio190/direction + echo out > /sys/class/gpio/gpio191/direction + + #LP Mode Port 16-31 + echo "pca9535 0x21" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN5_DEVICE}/new_device + echo 160 > /sys/class/gpio/export + echo 161 > /sys/class/gpio/export + echo 162 > /sys/class/gpio/export + echo 163 > /sys/class/gpio/export + echo 164 > /sys/class/gpio/export + echo 165 > /sys/class/gpio/export + echo 166 > /sys/class/gpio/export + echo 167 > /sys/class/gpio/export + echo 168 > /sys/class/gpio/export + echo 169 > /sys/class/gpio/export + echo 170 > /sys/class/gpio/export + echo 171 > /sys/class/gpio/export + echo 172 > /sys/class/gpio/export + echo 173 > /sys/class/gpio/export + echo 174 > /sys/class/gpio/export + echo 175 > /sys/class/gpio/export + echo out > /sys/class/gpio/gpio160/direction + echo out > /sys/class/gpio/gpio161/direction + echo out > /sys/class/gpio/gpio162/direction + echo out > /sys/class/gpio/gpio163/direction + echo out > /sys/class/gpio/gpio164/direction + echo out > /sys/class/gpio/gpio165/direction + echo out > /sys/class/gpio/gpio166/direction + echo out > /sys/class/gpio/gpio167/direction + echo out > /sys/class/gpio/gpio168/direction + echo out > /sys/class/gpio/gpio169/direction + echo out > /sys/class/gpio/gpio170/direction + echo out > /sys/class/gpio/gpio171/direction + echo out > /sys/class/gpio/gpio172/direction + echo out > /sys/class/gpio/gpio173/direction + echo out > /sys/class/gpio/gpio174/direction + echo out > /sys/class/gpio/gpio175/direction + + #RST Port 0-15 + echo "pca9535 0x22" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN5_DEVICE}/new_device + echo 144 > /sys/class/gpio/export + echo 145 > /sys/class/gpio/export + echo 146 > /sys/class/gpio/export + echo 147 > /sys/class/gpio/export + echo 148 > /sys/class/gpio/export + echo 149 > /sys/class/gpio/export + echo 150 > /sys/class/gpio/export + echo 151 > /sys/class/gpio/export + echo 152 > /sys/class/gpio/export + echo 153 > /sys/class/gpio/export + echo 154 > /sys/class/gpio/export + echo 155 > /sys/class/gpio/export + echo 156 > /sys/class/gpio/export + echo 157 > /sys/class/gpio/export + echo 158 > /sys/class/gpio/export + echo 159 > /sys/class/gpio/export + echo out > /sys/class/gpio/gpio144/direction + echo out > /sys/class/gpio/gpio145/direction + echo out > /sys/class/gpio/gpio146/direction + echo out > /sys/class/gpio/gpio147/direction + echo out > /sys/class/gpio/gpio148/direction + echo out > /sys/class/gpio/gpio149/direction + echo out > /sys/class/gpio/gpio150/direction + echo out > /sys/class/gpio/gpio151/direction + echo out > /sys/class/gpio/gpio152/direction + echo out > /sys/class/gpio/gpio153/direction + echo out > /sys/class/gpio/gpio154/direction + echo out > /sys/class/gpio/gpio155/direction + echo out > /sys/class/gpio/gpio156/direction + echo out > /sys/class/gpio/gpio157/direction + echo out > /sys/class/gpio/gpio158/direction + echo out > /sys/class/gpio/gpio159/direction + echo 1 > /sys/class/gpio/gpio144/active_low + echo 1 > /sys/class/gpio/gpio145/active_low + echo 1 > /sys/class/gpio/gpio146/active_low + echo 1 > /sys/class/gpio/gpio147/active_low + echo 1 > /sys/class/gpio/gpio148/active_low + echo 1 > /sys/class/gpio/gpio149/active_low + echo 1 > /sys/class/gpio/gpio150/active_low + echo 1 > /sys/class/gpio/gpio151/active_low + echo 1 > /sys/class/gpio/gpio152/active_low + echo 1 > /sys/class/gpio/gpio153/active_low + echo 1 > /sys/class/gpio/gpio154/active_low + echo 1 > /sys/class/gpio/gpio155/active_low + echo 1 > /sys/class/gpio/gpio156/active_low + echo 1 > /sys/class/gpio/gpio157/active_low + echo 1 > /sys/class/gpio/gpio158/active_low + echo 1 > /sys/class/gpio/gpio159/active_low + echo 0 > /sys/class/gpio/gpio144/value + echo 0 > /sys/class/gpio/gpio145/value + echo 0 > /sys/class/gpio/gpio146/value + echo 0 > /sys/class/gpio/gpio147/value + echo 0 > /sys/class/gpio/gpio148/value + echo 0 > /sys/class/gpio/gpio149/value + echo 0 > /sys/class/gpio/gpio150/value + echo 0 > /sys/class/gpio/gpio151/value + echo 0 > /sys/class/gpio/gpio152/value + echo 0 > /sys/class/gpio/gpio153/value + echo 0 > /sys/class/gpio/gpio154/value + echo 0 > /sys/class/gpio/gpio155/value + echo 0 > /sys/class/gpio/gpio156/value + echo 0 > /sys/class/gpio/gpio157/value + echo 0 > /sys/class/gpio/gpio158/value + echo 0 > /sys/class/gpio/gpio159/value + + #RST Port 16-31 + echo "pca9535 0x23" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN5_DEVICE}/new_device + echo 128 > /sys/class/gpio/export + echo 129 > /sys/class/gpio/export + echo 130 > /sys/class/gpio/export + echo 131 > /sys/class/gpio/export + echo 132 > /sys/class/gpio/export + echo 133 > /sys/class/gpio/export + echo 134 > /sys/class/gpio/export + echo 135 > /sys/class/gpio/export + echo 136 > /sys/class/gpio/export + echo 137 > /sys/class/gpio/export + echo 138 > /sys/class/gpio/export + echo 139 > /sys/class/gpio/export + echo 140 > /sys/class/gpio/export + echo 141 > /sys/class/gpio/export + echo 142 > /sys/class/gpio/export + echo 143 > /sys/class/gpio/export + echo out > /sys/class/gpio/gpio128/direction + echo out > /sys/class/gpio/gpio129/direction + echo out > /sys/class/gpio/gpio130/direction + echo out > /sys/class/gpio/gpio131/direction + echo out > /sys/class/gpio/gpio132/direction + echo out > /sys/class/gpio/gpio133/direction + echo out > /sys/class/gpio/gpio134/direction + echo out > /sys/class/gpio/gpio135/direction + echo out > /sys/class/gpio/gpio136/direction + echo out > /sys/class/gpio/gpio137/direction + echo out > /sys/class/gpio/gpio138/direction + echo out > /sys/class/gpio/gpio139/direction + echo out > /sys/class/gpio/gpio140/direction + echo out > /sys/class/gpio/gpio141/direction + echo out > /sys/class/gpio/gpio142/direction + echo out > /sys/class/gpio/gpio143/direction + echo 1 > /sys/class/gpio/gpio128/active_low + echo 1 > /sys/class/gpio/gpio129/active_low + echo 1 > /sys/class/gpio/gpio130/active_low + echo 1 > /sys/class/gpio/gpio131/active_low + echo 1 > /sys/class/gpio/gpio132/active_low + echo 1 > /sys/class/gpio/gpio133/active_low + echo 1 > /sys/class/gpio/gpio134/active_low + echo 1 > /sys/class/gpio/gpio135/active_low + echo 1 > /sys/class/gpio/gpio136/active_low + echo 1 > /sys/class/gpio/gpio137/active_low + echo 1 > /sys/class/gpio/gpio138/active_low + echo 1 > /sys/class/gpio/gpio139/active_low + echo 1 > /sys/class/gpio/gpio140/active_low + echo 1 > /sys/class/gpio/gpio141/active_low + echo 1 > /sys/class/gpio/gpio142/active_low + echo 1 > /sys/class/gpio/gpio143/active_low + echo 0 > /sys/class/gpio/gpio128/value + echo 0 > /sys/class/gpio/gpio129/value + echo 0 > /sys/class/gpio/gpio130/value + echo 0 > /sys/class/gpio/gpio131/value + echo 0 > /sys/class/gpio/gpio132/value + echo 0 > /sys/class/gpio/gpio133/value + echo 0 > /sys/class/gpio/gpio134/value + echo 0 > /sys/class/gpio/gpio135/value + echo 0 > /sys/class/gpio/gpio136/value + echo 0 > /sys/class/gpio/gpio137/value + echo 0 > /sys/class/gpio/gpio138/value + echo 0 > /sys/class/gpio/gpio139/value + echo 0 > /sys/class/gpio/gpio140/value + echo 0 > /sys/class/gpio/gpio141/value + echo 0 > /sys/class/gpio/gpio142/value + echo 0 > /sys/class/gpio/gpio143/value + +} + +#GPIO DeInit +function _i2c_gpio_deinit { + echo "0x20" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN4_DEVICE}/delete_device + echo "0x21" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN4_DEVICE}/delete_device + echo "0x22" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN4_DEVICE}/delete_device + echo "0x23" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN4_DEVICE}/delete_device + echo "0x20" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN5_DEVICE}/delete_device + echo "0x21" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN5_DEVICE}/delete_device + echo "0x22" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN5_DEVICE}/delete_device + echo "0x23" > /sys/bus/i2c/devices/i2c-${NUM_MUX1_CHAN5_DEVICE}/delete_device +} + +#I2C CPLD init +function _i2c_cpld_init { + echo "=========================================================" + echo "# Description: I2C CPLD Init..." + echo "=========================================================" + + ## modprobe i2c_cpld + modprobe i2c_cpld + ## Add CPLD device + echo "i2c_cpld 0x33" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_I801_DEVICE}/new_device + + echo "done..." +} + +#Set FAN Tray LED +function _i2c_led_fan_tray_status_set { + echo "FAN Tray Status Setup" + #FAN Status get + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN2_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan2_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN4_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan4_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN6_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan6_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + FAN8_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan8_alarm` + + if [ "${FAN1_ALARM}" == "0" ] && [ "${FAN2_ALARM}" == "0" ]; then + FAN_TRAY=1 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=1 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + + if [ "${FAN3_ALARM}" == "0" ] && [ "${FAN4_ALARM}" == "0" ]; then + FAN_TRAY=2 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=2 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + + if [ "${FAN5_ALARM}" == "0" ] && [ "${FAN6_ALARM}" == "0" ]; then + FAN_TRAY=3 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=3 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + + if [ "${FAN7_ALARM}" == "0" ] && [ "${FAN8_ALARM}" == "0" ]; then + FAN_TRAY=4 + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=4 + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi +} + +#Set FAN LED +function _i2c_led_fan_status_set { + echo "FAN Status Setup" + #PSU Status set + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN2_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan2_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN4_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan4_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN6_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan6_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + FAN8_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan8_alarm` + + if [ "${FAN1_ALARM}" == "0" ] && [ "${FAN2_ALARM}" == "0" ] \ + && [ "${FAN3_ALARM}" == "0" ] && [ "${FAN4_ALARM}" == "0" ] \ + && [ "${FAN5_ALARM}" == "0" ] && [ "${FAN6_ALARM}" == "0" ] \ + && [ "${FAN7_ALARM}" == "0" ] && [ "${FAN8_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + fi +} + +#Set Power Supply LED +function _i2c_led_psu_status_set { + echo "PSU LED Status Setup" + + #PSU Status set + _i2c_psu_status + + #PSU1 Status + if [ "${psu1Exist}" == ${PSU_EXIST} ]; then + if [ "${psu1PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + fi + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + fi + + #PSU2 Status + if [ "${psu2Exist}" == ${PSU_EXIST} ]; then + if [ "${psu2PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + fi + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + fi +} + +#LED Test +function _i2c_led_test { + echo "=========================================================" + echo "# Description: I2C LED TEST..." + echo "=========================================================" + #sys led (green) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x7F + _pause 'Check SYS LED green light and Press [Enter] key to continue...' + #sys led (amber) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xBF + _pause 'Check SYS LED amber light and Press [Enter] key to continue...' + + #FAN led (green) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xF7 + _pause 'Check FAN LED green light and Press [Enter] key to continue...' + #FAN led (amber) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFB + _pause 'Check FAN LED amber light and Press [Enter] key to continue...' + + #PSU2 led (green) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xDF + _pause 'Check PSU2 LED green light and Press [Enter] key to continue...' + #PSU2 led (amber) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xEF + _pause 'Check PSU2 LED amber light and Press [Enter] key to continue...' + + #PSU1 led (green) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFD + _pause 'Check PSU1 LED green light and Press [Enter] key to continue...' + #PSU1 led (amber) + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFE + _pause 'Check PSU1 LED amber light and Press [Enter] key to continue...' + + #Turn OFF All LED + i2cset -y ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + _pause 'Check turn off all LEDs and Press [Enter] key to continue...' + echo "done..." +} + +#Set QSFP Port variable +function _qsfp_port_i2c_var_set { + local port=$1 + case ${port} in + 1|2|3|4|5|6|7|8) + i2cbus=${NUM_MUX1_CHAN4_DEVICE} + regAddr=0x20 + dataAddr=0 + eeprombusbase=${NUM_MUX3_CHAN0_DEVICE} + gpioBase=240 + ;; + 9|10|11|12|13|14|15|16) + i2cbus=${NUM_MUX1_CHAN4_DEVICE} + regAddr=0x20 + dataAddr=1 + eeprombusbase=${NUM_MUX4_CHAN0_DEVICE} + gpioBase=240 + ;; + 17|18|19|20|21|22|23|24) + i2cbus=${NUM_MUX1_CHAN4_DEVICE} + regAddr=0x21 + dataAddr=0 + eeprombusbase=${NUM_MUX5_CHAN0_DEVICE} + gpioBase=$((224 - 16)) + ;; + 25|26|27|28|29|30|31|32) + i2cbus=${NUM_MUX1_CHAN4_DEVICE} + regAddr=0x21 + dataAddr=1 + eeprombusbase=${NUM_MUX6_CHAN0_DEVICE} + gpioBase=$((224 - 16)) + ;; + 33) + i2cbus=${NUM_MUX1_CHAN7_DEVICE} + regAddr=0x22 + dataAddr=0 + ;; + 34) + i2cbus=${NUM_MUX1_CHAN7_DEVICE} + regAddr=0x22 + dataAddr=1 + ;; + *) + echo "Please input 1~32" + ;; + esac +} + +#Set QSFP Port variable +function _qsfp_eeprom_var_set { + local port=$1 + eeprombusidx=$(( ${port} % 8)) + case $eeprombusidx in + 1) + eeprombus=$(( $eeprombusbase + 1 )) + eepromAddr=0x50 + ;; + 2) + eeprombus=$(( $eeprombusbase + 0 )) + eepromAddr=0x50 + ;; + 3) + eeprombus=$(( $eeprombusbase + 3 )) + eepromAddr=0x50 + ;; + 4) + eeprombus=$(( $eeprombusbase + 2 )) + eepromAddr=0x50 + ;; + 5) + eeprombus=$(( $eeprombusbase + 5 )) + eepromAddr=0x50 + ;; + 6) + eeprombus=$(( $eeprombusbase + 4 )) + eepromAddr=0x50 + ;; + 7) + eeprombus=$(( $eeprombusbase + 7 )) + eepromAddr=0x50 + ;; + 0) + eeprombus=$(( $eeprombusbase + 6 )) + eepromAddr=0x50 + ;; + esac +} + +#Get QSFP EEPROM Information +function _i2c_qsfp_eeprom_get { + + _qsfp_port_i2c_var_set ${QSFP_PORT} + + #status: 0 -> Down, 1 -> Up + status=`cat /sys/class/gpio/gpio$(( $(($gpioBase + (${QSFP_PORT} - 1) ^ 1)) ))/value` + echo $status + + if [ $status = 0 ]; then + exit + fi + + _qsfp_eeprom_var_set ${QSFP_PORT} + + cat ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/eeprom | hexdump -C +} + +#Init QSFP EEPROM +function _i2c_qsfp_eeprom_init { + echo -n "QSFP EEPROM INIT..." + + #Action check + action=$1 + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init 1-32 ports EEPROM + local i + for i in {1..32}; + do + _qsfp_port_i2c_var_set ${i} + + _qsfp_eeprom_var_set ${i} + + if [ "${action}" == "new" ] && \ + ! [ -L ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr) ]; then + echo "sff8436 $eepromAddr" > ${PATH_SYS_I2C_DEVICES}/i2c-$eeprombus/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr) ]; then + echo "$eepromAddr" > ${PATH_SYS_I2C_DEVICES}/i2c-$eeprombus/delete_device + fi + done + echo "DONE" +} + +#Init Main Board EEPROM +function _i2c_mb_eeprom_init { + echo -n "Main Board EEPROM INIT..." + + #Action check + action=$1 + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init 1-32 ports EEPROM + if [ "${action}" == "new" ] && \ + ! [ -L ${PATH_SYS_I2C_DEVICES}/${NUM_MUX1_CHAN7_DEVICE}-0054 ]; then + echo "mb_eeprom 0x54" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN7_DEVICE}/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${PATH_SYS_I2C_DEVICES}/${NUM_MUX1_CHAN7_DEVICE}-0054 ]; then + echo "0x54" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN7_DEVICE}/delete_device + fi + echo "DONE" +} + +#PSU EEPROM init +function _i2c_psu_eeprom_init { + echo "=========================================================" + echo "# Description: I2C PSU EEPROM Init..." + echo "=========================================================" + + ## modprobe eeprom + modprobe eeprom + ## PUS(0) EEPROM + echo "eeprom 0x50" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN6_DEVICE}/new_device + + ## PUS(1) EEPROM + echo "eeprom 0x50" > ${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX1_CHAN7_DEVICE}/new_device + + echo "done..." +} + +#get QSFP Status +function _i2c_qsfp_status_get { + + _qsfp_port_i2c_var_set ${QSFP_PORT} + + #status: 0 -> Down, 1 -> Up + status=`cat /sys/class/gpio/gpio$(( $(($gpioBase + (${QSFP_PORT} - 1) ^ 1)) ))/value` + echo "status=$status" +} + +#get QSFP Type +function _i2c_qsfp_type_get { + + _qsfp_port_i2c_var_set ${QSFP_PORT} + + _qsfp_eeprom_var_set ${QSFP_PORT} + + #Get QSFP EEPROM info + qsfp_info=$(base64 ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/eeprom) + + identifier=$(echo $qsfp_info | base64 -d -i | hexdump -s 128 -n 1 -e '"%x"') + connector=$(echo $qsfp_info | base64 -d -i | hexdump -s 130 -n 1 -e '"%x"') + transceiver=$(echo $qsfp_info | base64 -d -i | hexdump -s 131 -n 1 -e '"%x"') + + echo "identifier=$identifier" + echo "connector=$connector" + echo "transceiver=$transceiver" +} + +#Get PSU EEPROM Information +function _i2c_psu_eeprom_get { + echo "=========================================================" + echo "# Description: I2C PSU EEPROM Get..." + echo "=========================================================" + + ## modprobe eeprom + modprobe eeprom + ## PUS(0) EEPROM + cat ${PATH_SYS_I2C_DEVICES}/${NUM_MUX1_CHAN6_DEVICE}-0050/eeprom | hexdump -C + + ## PUS(1) EEPROM + cat ${PATH_SYS_I2C_DEVICES}/${NUM_MUX1_CHAN7_DEVICE}-0050/eeprom | hexdump -C + + echo "done..." +} + +#Get MotherBoard EEPROM Information +function _i2c_mb_eeprom_get { + echo "=========================================================" + echo "# Description: I2C MB EEPROM Get..." + echo "=========================================================" + + ## MB EEPROM + cat ${PATH_SYS_I2C_DEVICES}/${NUM_MUX1_CHAN7_DEVICE}-0054/eeprom | hexdump -C + echo "done..." +} + +#Set System Status LED +function _i2c_sys_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x80 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x80 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x40 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x40 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Set PSU2 LED +function _i2c_psu2_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x20 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x20 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x10 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x10 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Set FAN Tray LED +function _i2c_fan_tray_led { + case ${FAN_TRAY} in + 1) + i2cAddr=0x20 + ioPort=2 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x01 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x02 + fi + ;; + 2) + i2cAddr=0x20 + ioPort=2 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + 3) + i2cAddr=0x20 + ioPort=3 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x01 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x02 + fi + ;; + 4) + i2cAddr=0x20 + ioPort=3 + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + *) + echo "Please input 1~4" + exit + ;; + esac + + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m $mask -y -r ${NUM_MUX1_CHAN7_DEVICE} $i2cAddr $ioPort 0x33 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m $mask -y -r ${NUM_MUX1_CHAN7_DEVICE} $i2cAddr $ioPort 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m $mask -y -r ${NUM_MUX1_CHAN7_DEVICE} $i2cAddr $ioPort 0x33 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m $mask -y -r ${NUM_MUX1_CHAN7_DEVICE} $i2cAddr $ioPort 0x00 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Set FAN LED +function _i2c_fan_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x08 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x08 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x04 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x04 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Set PSU1 LED +function _i2c_psu1_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x02 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x02 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + i2cset -m 0x01 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + i2cset -m 0x01 -y -r ${NUM_MUX1_CHAN7_DEVICE} 0x22 2 0xFF + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "done..." +} + +#Get Board Version and Type +function _i2c_board_type_get { + boardType=`cat ${PATH_SYS_I2C_DEVICES}/0-0033/cpld_board_type` + boardBuildRev=$((($boardType) & 0x03)) + boardHwRev=$((($boardType) >> 2 & 0x03)) + boardId=$((($boardType) >> 4)) + printf "BOARD_ID is 0x%02x, HW Rev %d, Build Rev %d\n" $boardId $boardHwRev $boardBuildRev + +} + +#Get CPLD Version +function _i2c_cpld_version { + cpldRev=`cat ${PATH_SYS_I2C_DEVICES}/0-0033/cpld_version` + cpldRelease=$((($cpldRev) >> 6 & 0x01)) + cpldVersion=$((($cpldRev) & 0x3F)) + printf "CPLD is %s version(0:RD 1:Release), Revision is 0x%02x\n" $cpldRelease $cpldVersion + +} + +#Get PSU Status +function _i2c_psu_status { + psuPresent=`cat ${PATH_SYS_I2C_DEVICES}/0-0033/cpld_pw_abs` + psu1Exist=$(($((($psuPresent) & 0x01))?0:1)) + psu2Exist=$(($((($psuPresent) & 0x02))?0:1)) + psuPwGood=`cat ${PATH_SYS_I2C_DEVICES}/0-0033/cpld_pw_good` + psu1PwGood=$(($((($psuPwGood) >> 3 & 0x01))?1:0)) + psu2PwGood=$(($((($psuPwGood) >> 3 & 0x02))?1:0)) + printf "PSU1 Exist:%d PSU1 PW Good:%d\n" $psu1Exist $psu1PwGood + printf "PSU2 Exist:%d PSU2 PW Good:%d\n" $psu2Exist $psu2PwGood +} + +#Get Front Sensor Temperature +function _i2c_front_temp { + #Front MAC + sensors | grep 'Front MAC Temp' -A 1 +} + +#Get Rear Sensor Temperature +function _i2c_rear_temp { + #Rear MAC + sensors | grep 'Rear MAC Temp' -A 1 +} + +#Increase read socket buffer for CoPP Test +function _config_rmem { + echo "109430400" > /proc/sys/net/core/rmem_max +} + +#Main Function +function _main { + tart_time_str=`date` + start_time_sec=$(date +%s) + + if [ "${EXEC_FUNC}" == "help" ]; then + _help + elif [ "${EXEC_FUNC}" == "i2c_init" ]; then + _i2c_init + elif [ "${EXEC_FUNC}" == "i2c_deinit" ]; then + _i2c_deinit + elif [ "${EXEC_FUNC}" == "i2c_temp_init" ]; then + _i2c_temp_init + elif [ "${EXEC_FUNC}" == "i2c_fan_init" ]; then + _i2c_fan_init + elif [ "${EXEC_FUNC}" == "i2c_volmon_init" ]; then + _i2c_volmon_init + elif [ "${EXEC_FUNC}" == "i2c_io_exp_init" ]; then + _i2c_io_exp_init + elif [ "${EXEC_FUNC}" == "i2c_gpio_init" ]; then + _i2c_gpio_init + elif [ "${EXEC_FUNC}" == "i2c_gpio_deinit" ]; then + _i2c_gpio_deinit + elif [ "${EXEC_FUNC}" == "i2c_led_test" ]; then + _i2c_led_test + elif [ "${EXEC_FUNC}" == "i2c_mb_eeprom_get" ]; then + _i2c_mb_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_psu_eeprom_get" ]; then + _i2c_psu_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_eeprom_get" ]; then + _i2c_qsfp_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_eeprom_init" ]; then + _i2c_qsfp_eeprom_init ${QSFP_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_mb_eeprom_init" ]; then + _i2c_mb_eeprom_init ${MB_EEPROM_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_qsfp_status_get" ]; then + _i2c_qsfp_status_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_type_get" ]; then + _i2c_qsfp_type_get + elif [ "${EXEC_FUNC}" == "i2c_led_psu_status_set" ]; then + _i2c_led_psu_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_status_set" ]; then + _i2c_led_fan_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_tray_status_set" ]; then + _i2c_led_fan_tray_status_set + elif [ "${EXEC_FUNC}" == "i2c_sys_led" ]; then + _i2c_sys_led + elif [ "${EXEC_FUNC}" == "i2c_fan_led" ]; then + _i2c_fan_led + elif [ "${EXEC_FUNC}" == "i2c_fan_tray_led" ]; then + _i2c_fan_tray_led + elif [ "${EXEC_FUNC}" == "i2c_psu1_led" ]; then + _i2c_psu1_led + elif [ "${EXEC_FUNC}" == "i2c_psu2_led" ]; then + _i2c_psu2_led + elif [ "${EXEC_FUNC}" == "i2c_board_type_get" ]; then + _i2c_board_type_get + elif [ "${EXEC_FUNC}" == "i2c_cpld_version" ]; then + _i2c_cpld_version + elif [ "${EXEC_FUNC}" == "i2c_psu_status" ]; then + _i2c_psu_status + elif [ "${EXEC_FUNC}" == "i2c_front_temp" ]; then + _i2c_front_temp + elif [ "${EXEC_FUNC}" == "i2c_rear_temp" ]; then + _i2c_rear_temp + elif [ "${EXEC_FUNC}" == "i2c_test_all" ]; then + _i2c_init + _i2c_temp_init + _i2c_fan_init + _i2c_io_exp_init + _i2c_led_test + _i2c_psu_eeprom_get + _i2c_mb_eeprom_get + _i2c_board_type_get + _i2c_cpld_version + _i2c_psu_status + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + end_time_str=`date` + end_time_sec=$(date +%s) + diff_time=$[ ${end_time_sec} - ${start_time_sec} ] + echo "Start Time: ${start_time_str} (${start_time_sec})" + echo "End Time : ${end_time_str} (${end_time_sec})" + echo "Total Execution Time: ${diff_time} sec" + + echo "done!!!" +} + +_main diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9100/utils/qsfp_monitor.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/utils/qsfp_monitor.sh new file mode 100755 index 000000000000..249f179216a6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/utils/qsfp_monitor.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# Copyright (C) 2017 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=3 +I2C_UTILS="/usr/sbin/i2c_utils.sh" +QSFP_SI_SCRIPT="/usr/sbin/qsfp_si_cfg.sh" +QSFP_ARRAY=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + +#QSFP SI monitor +function _qsfp_si_monitor { + local i + local status + for i in {0..31}; + do + status=`${I2C_UTILS} i2c_qsfp_status_get $(expr $i + 1) | egrep '^status=.*$' | sed -e 's/status=//g'` + if [ "${status}" == "1" ]; then + _qsfp_type_check $i + fi + done +} + +#QSFP type +function _qsfp_type_check { + local port=$1 + local qsfp_type=`${I2C_UTILS} i2c_qsfp_type_get $(expr $port + 1)` + local identifier=`echo "$qsfp_type" | grep '^identifier=.*$' | sed -e 's/identifier=//g'` + if [ "${identifier}" == "11" ]; then + connector=`echo "$qsfp_type" | grep '^connector=.*$' | sed -e 's/connector=//g'` + case ${connector} in + 21|23) + #DAC + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to DAC" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} dac $port >/dev/null + fi + ;; + *) + #Optical + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to Optical" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} optical $port >/dev/null + fi + ;; + esac + fi +} + +#Docker exist check +function _docker_swss_check { + while true + do + # Check if syncd starts + result=`docker exec -i swss bash -c "echo -en \"SELECT 1\\nHLEN HIDDEN\" | redis-cli | sed -n 2p"` #TBD FIX ME + if [ "$result" == "3" ]; then + return + fi + sleep $INTERVAL + done +} + +#Docker exist check +function _qsfp_si_cfg_script_check { + + if [ -f ${QSFP_SI_SCRIPT} ] && [ -x ${QSFP_SI_SCRIPT} ]; then + echo "SI Script exists. Start monitor." + return + else + echo "SI Script not exist. Exit monitor." + exit + fi +} + +# main function +function _main { + #Check SI Script + _qsfp_si_cfg_script_check + #Check docker swss is running + _docker_swss_check + while true + do + _qsfp_si_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9100/utils/qsfp_si_cfg.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/utils/qsfp_si_cfg.sh new file mode 100755 index 000000000000..f2bf35bc7715 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/utils/qsfp_si_cfg.sh @@ -0,0 +1,339 @@ +#!/bin/bash +# Copyright (C) 2017 Ingrasys, Inc. +# +# 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 3 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, see . + +VERSION="1.0.0" +TRUE=200 +FALSE=404 + +TYPE=${1} +PORT=${2} + +#QSFP Optical +function _qsfp_optical_si_set { + case ${PORT} in + 0) + bcmcmd "phy ce0 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x39;phy ce0 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x37;phy ce0 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0;phy ce0 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x39" + bcmcmd "phy ce0 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x37;phy ce0 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce0 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x35;phy ce0 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x37" + bcmcmd "phy ce0 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0;phy ce0 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce0 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce0 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + ;; + 1) + bcmcmd "phy ce1 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x3E;phy ce1 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x32;phy ce1 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0;phy ce1 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x38" + bcmcmd "phy ce1 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x38;phy ce1 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xE AMS_TX_DRIVERMODE=0x0;phy ce1 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x33;phy ce1 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3D" + bcmcmd "phy ce1 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0;phy ce1 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x35;phy ce1 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x3B;phy ce1 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xE AMS_TX_DRIVERMODE=0x0" + ;; + 2) + bcmcmd "phy ce2 CL93N72_UT_CTL2r CL93N72_TXFIR_POST=0x35;phy ce2 CL93N72_UT_CTL3r CL93N72_TXFIR_MAIN=0x3B;phy ce2 AMS_TX_CTL2r AMS_TX_AMP_CTL=0xB AMS_TX_DRIVERMODE=0x0" + ;; + 3) + bcmcmd "phy ce3 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x3B;phy ce3 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x35;phy ce3 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0;phy ce3 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x35" + bcmcmd "phy ce3 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x37;phy ce3 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xC AMS_TX_DRIVERMODE=0x0;phy ce3 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x35;phy ce3 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x37" + bcmcmd "phy ce3 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xB AMS_TX_DRIVERMODE=0x0;phy ce3 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x35;phy ce3 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x37;phy ce3 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xB AMS_TX_DRIVERMODE=0x0" + ;; + 4) + bcmcmd "phy ce4 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x30;phy ce4 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x40;phy ce4 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce4 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x33" + bcmcmd "phy ce4 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x3D;phy ce4 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce4 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x30;phy ce4 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x40" + bcmcmd "phy ce4 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce4 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce4 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce4 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0" + ;; + 5) + bcmcmd "phy ce5 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x30;phy ce5 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x40;phy ce5 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce5 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x35" + bcmcmd "phy ce5 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x3B;phy ce5 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0;phy ce5 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x30;phy ce5 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x40" + bcmcmd "phy ce5 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce5 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce5 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce5 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0" + ;; + 6) + bcmcmd "phy ce6 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x30;phy ce6 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x40;phy ce6 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce6 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x36" + bcmcmd "phy ce6 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x3A;phy ce6 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xC AMS_TX_DRIVERMODE=0x0;phy ce6 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x30;phy ce6 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x40" + bcmcmd "phy ce6 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce6 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce6 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce6 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0" + ;; + 7) + bcmcmd "phy ce7 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x30;phy ce7 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x40;phy ce7 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce7 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x30" + bcmcmd "phy ce7 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x40;phy ce7 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce7 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x30;phy ce7 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x40" + bcmcmd "phy ce7 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce7 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce7 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce7 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0" + ;; + 8) + bcmcmd "phy ce8 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x25;phy ce8 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x4B;phy ce8 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce8 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x2C" + bcmcmd "phy ce8 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x44;phy ce8 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0;phy ce8 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x25;phy ce8 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x4B" + bcmcmd "phy ce8 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce8 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x25;phy ce8 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x4B;phy ce8 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0" + ;; + 9) + bcmcmd "phy ce9 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x25;phy ce9 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x4B;phy ce9 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce9 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x2B" + bcmcmd "phy ce9 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x45;phy ce9 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0;phy ce9 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x25;phy ce9 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x4B" + bcmcmd "phy ce9 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce9 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x25;phy ce9 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x4B;phy ce9 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0" + ;; + 10) + bcmcmd "phy ce10 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x25;phy ce10 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x4B;phy ce10 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce10 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x2B" + bcmcmd "phy ce10 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x45;phy ce10 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x7 AMS_TX_DRIVERMODE=0x0;phy ce10 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x25;phy ce10 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x4B" + bcmcmd "phy ce10 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce10 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x25;phy ce10 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x4B;phy ce10 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0" + ;; + 11) + bcmcmd "phy ce11 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x25;phy ce11 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x4B;phy ce11 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce11 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x2C" + bcmcmd "phy ce11 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x44;phy ce11 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce11 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x25;phy ce11 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x4B" + bcmcmd "phy ce11 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce11 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x25;phy ce11 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x4B;phy ce11 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0" + ;; + 12) + bcmcmd "phy ce12 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x25;phy ce12 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x4B;phy ce12 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce12 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x25" + bcmcmd "phy ce12 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x4B;phy ce12 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x5 AMS_TX_DRIVERMODE=0x0;phy ce12 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x25;phy ce12 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x4B" + bcmcmd "phy ce12 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce12 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x25;phy ce12 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x4B;phy ce12 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0" + ;; + 13) + bcmcmd "phy ce13 CL93N72_UT_CTL2r CL93N72_TXFIR_POST=0x25;phy ce13 CL93N72_UT_CTL3r CL93N72_TXFIR_MAIN=0x4B;phy ce13 AMS_TX_CTL2r AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0" + ;; + 14) + bcmcmd "phy ce14 CL93N72_UT_CTL2r CL93N72_TXFIR_POST=0x25;phy ce14 CL93N72_UT_CTL3r CL93N72_TXFIR_MAIN=0x4B;phy ce14 AMS_TX_CTL2r AMS_TX_AMP_CTL=0x7 AMS_TX_DRIVERMODE=0x0" + ;; + 15) + bcmcmd "phy ce15 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x25;phy ce15 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x4B;phy ce15 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0x7 AMS_TX_DRIVERMODE=0x0;phy ce15 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x25" + bcmcmd "phy ce15 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x4B;phy ce15 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x5 AMS_TX_DRIVERMODE=0x0;phy ce15 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x25;phy ce15 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x4B" + bcmcmd "phy ce15 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x7 AMS_TX_DRIVERMODE=0x0;phy ce15 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x25;phy ce15 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x4B;phy ce15 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x7 AMS_TX_DRIVERMODE=0x0" + ;; + 16) + bcmcmd "phy ce16 CL93N72_UT_CTL2r CL93N72_TXFIR_POST=0x27;phy ce16 CL93N72_UT_CTL3r CL93N72_TXFIR_MAIN=0x49;phy ce16 AMS_TX_CTL2r AMS_TX_AMP_CTL=0x4 AMS_TX_DRIVERMODE=0x0" + ;; + 17) + bcmcmd "phy ce17 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x25;phy ce17 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x4B;phy ce17 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce17 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x30" + bcmcmd "phy ce17 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x40;phy ce17 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0;phy ce17 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x30;phy ce17 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x40" + bcmcmd "phy ce17 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0;phy ce17 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x30;phy ce17 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x40;phy ce17 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + ;; + 18) + bcmcmd "phy ce18 CL93N72_UT_CTL2r CL93N72_TXFIR_POST=0x20;phy ce18 CL93N72_UT_CTL3r CL93N72_TXFIR_MAIN=0x50;phy ce18 AMS_TX_CTL2r AMS_TX_AMP_CTL=0x2 AMS_TX_DRIVERMODE=0x0" + ;; + 19) + bcmcmd "phy ce19 CL93N72_UT_CTL2r CL93N72_TXFIR_POST=0x20;phy ce19 CL93N72_UT_CTL3r CL93N72_TXFIR_MAIN=0x50;phy ce19 AMS_TX_CTL2r AMS_TX_AMP_CTL=0x2 AMS_TX_DRIVERMODE=0x0" + ;; + 20) + bcmcmd "phy ce20 CL93N72_UT_CTL2r CL93N72_TXFIR_POST=0x20;phy ce20 CL93N72_UT_CTL3r CL93N72_TXFIR_MAIN=0x50;phy ce20 AMS_TX_CTL2r AMS_TX_AMP_CTL=0x2 AMS_TX_DRIVERMODE=0x0" + ;; + 21) + bcmcmd "phy ce21 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x17;phy ce21 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x59;phy ce21 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0x4 AMS_TX_DRIVERMODE=0x0;phy ce21 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x17" + bcmcmd "phy ce21 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x59;phy ce21 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x2 AMS_TX_DRIVERMODE=0x0;phy ce21 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x20;phy ce21 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x50" + bcmcmd "phy ce21 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x2 AMS_TX_DRIVERMODE=0x0;phy ce21 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x20;phy ce21 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x50;phy ce21 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x2 AMS_TX_DRIVERMODE=0x0" + ;; + 22) + bcmcmd "phy ce22 CL93N72_UT_CTL2r CL93N72_TXFIR_POST=0x25;phy ce22 CL93N72_UT_CTL3r CL93N72_TXFIR_MAIN=0x4B;phy ce22 AMS_TX_CTL2r AMS_TX_AMP_CTL=0x7 AMS_TX_DRIVERMODE=0x0" + ;; + 23) + bcmcmd "phy ce23 CL93N72_UT_CTL2r CL93N72_TXFIR_POST=0x25;phy ce23 CL93N72_UT_CTL3r CL93N72_TXFIR_MAIN=0x4B;phy ce23 AMS_TX_CTL2r AMS_TX_AMP_CTL=0x7 AMS_TX_DRIVERMODE=0x0" + ;; + 24) + bcmcmd "phy ce24 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x23;phy ce24 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x4D;phy ce24 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce24 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x29" + bcmcmd "phy ce24 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x47;phy ce24 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce24 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x29;phy ce24 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x47" + bcmcmd "phy ce24 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x4 AMS_TX_DRIVERMODE=0x0;phy ce24 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x29;phy ce24 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x47;phy ce24 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x4 AMS_TX_DRIVERMODE=0x0" + ;; + 25) + bcmcmd "phy ce25 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x29;phy ce25 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x47;phy ce25 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce25 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x29" + bcmcmd "phy ce25 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x47;phy ce25 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce25 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x29;phy ce25 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x47" + bcmcmd "phy ce25 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x4 AMS_TX_DRIVERMODE=0x0;phy ce25 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x29;phy ce25 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x47;phy ce25 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x4 AMS_TX_DRIVERMODE=0x0" + ;; + 26) + bcmcmd "phy ce26 CL93N72_UT_CTL2r CL93N72_TXFIR_POST=0x30;phy ce26 CL93N72_UT_CTL3r CL93N72_TXFIR_MAIN=0x40;phy ce26 AMS_TX_CTL2r AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0" + ;; + 27) + bcmcmd "phy ce27 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x30;phy ce27 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x40;phy ce27 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0;phy ce27 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x30" + bcmcmd "phy ce27 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x40;phy ce27 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0;phy ce27 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x30;phy ce27 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x40" + bcmcmd "phy ce27 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xB AMS_TX_DRIVERMODE=0x0;phy ce27 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce27 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce27 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0" + ;; + 28) + bcmcmd "phy ce28 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x30;phy ce28 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x40;phy ce28 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce28 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x30" + bcmcmd "phy ce28 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x40;phy ce28 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce28 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x30;phy ce28 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x40" + bcmcmd "phy ce28 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0;phy ce28 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce28 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce28 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0" + ;; + 29) + bcmcmd "phy ce29 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x30;phy ce29 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x40;phy ce29 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce29 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x30" + bcmcmd "phy ce29 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x40;phy ce29 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0;phy ce29 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x30;phy ce29 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x40" + bcmcmd "phy ce29 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0;phy ce29 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce29 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce29 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0" + ;; + 30) + bcmcmd "phy ce30 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x30;phy ce30 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x40;phy ce30 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce30 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x30" + bcmcmd "phy ce30 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x40;phy ce30 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0;phy ce30 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x30;phy ce30 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x40" + bcmcmd "phy ce30 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0;phy ce30 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce30 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce30 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0" + ;; + 31) + bcmcmd "phy ce31 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x39;phy ce31 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x37;phy ce31 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xC AMS_TX_DRIVERMODE=0x0;phy ce31 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x39" + bcmcmd "phy ce31 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x37;phy ce31 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xC AMS_TX_DRIVERMODE=0x0;phy ce31 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x30;phy ce31 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x40" + bcmcmd "phy ce31 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0;phy ce31 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce31 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce31 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0" + ;; + *) + echo "Unknown Port" + return + ;; + esac +} + +function _qsfp_dac_si_set { + case ${PORT} in + 0) + bcmcmd "phy ce0 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce0 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce0 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce0 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce0 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x1C;phy ce0 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x20;phy ce0 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x34;phy ce0 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce0 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce0 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce0 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce0 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce0 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x18;phy ce0 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x24;phy ce0 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x34;phy ce0 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + ;; + 1) + bcmcmd "phy ce1 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce1 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce1 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce1 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce1 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x1C;phy ce1 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x20;phy ce1 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x34;phy ce1 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce1 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce1 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce1 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce1 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce1 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x1C;phy ce1 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x20;phy ce1 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x34;phy ce1 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + ;; + 2) + bcmcmd "phy ce2 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce2 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce2 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce2 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce2 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x18;phy ce2 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x24;phy ce2 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x34;phy ce2 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xD AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce2 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce2 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce2 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce2 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce2 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x14;phy ce2 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x20;phy ce2 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x3C;phy ce2 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xD AMS_TX_DRIVERMODE=0x0" + ;; + 3) + bcmcmd "phy ce3 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce3 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce3 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce3 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce3 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x14;phy ce3 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x20;phy ce3 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x3C;phy ce3 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xD AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce3 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce3 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce3 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce3 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce3 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x18;phy ce3 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x24;phy ce3 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x34;phy ce3 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xD AMS_TX_DRIVERMODE=0x0" + ;; + 4) + bcmcmd "phy ce4 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce4 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce4 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce4 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce4 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x0;phy ce4 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x33;phy ce4 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x3D;phy ce4 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xB AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce4 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce4 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce4 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce4 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce4 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x0;phy ce4 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce4 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce4 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0" + ;; + 5) + bcmcmd "phy ce5 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce5 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce5 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce5 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce5 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x0;phy ce5 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x30;phy ce5 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x40;phy ce5 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xB AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce5 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce5 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce5 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce5 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce5 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x0;phy ce5 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce5 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce5 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0" + ;; + 6) + bcmcmd "phy ce6 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce6 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce6 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce6 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce6 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x0;phy ce6 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x36;phy ce6 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x3A;phy ce6 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xC AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce6 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce6 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce6 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce6 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce6 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x0;phy ce6 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce6 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce6 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0" + ;; + 7) + bcmcmd "phy ce7 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce7 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce7 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce7 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce7 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x0;phy ce7 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x30;phy ce7 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x40;phy ce7 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce7 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce7 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce7 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce7 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce7 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x0;phy ce7 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce7 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce7 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0" + ;; + 8) + ;; + 9) + ;; + 10) + ;; + 11) + ;; + 12) + ;; + 13) + ;; + 14) + ;; + 15) + ;; + 16) + ;; + 17) + ;; + 18) + ;; + 19) + ;; + 20) + ;; + 21) + ;; + 22) + ;; + 23) + ;; + 24) + bcmcmd "phy ce24 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce24 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce24 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce24 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce24 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x0;phy ce24 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x29;phy ce24 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x47;phy ce24 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce24 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0X1F;phy ce24 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce24 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce24 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce24 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x0;phy ce24 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x29;phy ce24 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x47;phy ce24 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x4 AMS_TX_DRIVERMODE=0x0" + ;; + 25) + bcmcmd "phy ce25 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce25 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce25 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce25 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce25 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x0;phy ce25 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x29;phy ce25 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x47;phy ce25 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x8 AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce25 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce25 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce25 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce25 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce25 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x0;phy ce25 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x29;phy ce25 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x47;phy ce25 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x4 AMS_TX_DRIVERMODE=0x0" + ;; + 26) + bcmcmd "phy ce26 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce26 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce26 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce26 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce26 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x0;phy ce26 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x30;phy ce26 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x40;phy ce26 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xA AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce26 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce26 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce26 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce26 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce26 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x0;phy ce26 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce26 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce26 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0" + ;; + 27) + bcmcmd "phy ce27 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce27 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce27 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce27 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce27 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x0;phy ce27 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x30;phy ce27 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x40;phy ce27 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce27 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce27 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce27 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce27 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce27 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x0;phy ce27 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x30;phy ce27 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x40;phy ce27 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0x9 AMS_TX_DRIVERMODE=0x0" + ;; + 28) + bcmcmd "phy ce28 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce28 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce28 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce28 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce28 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x10;phy ce28 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x18;phy ce28 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x3C;phy ce28 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xC AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce28 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce28 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce28 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce28 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce28 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x10;phy ce28 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x1C;phy ce28 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x3C;phy ce28 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xC AMS_TX_DRIVERMODE=0x0" + ;; + 29) + bcmcmd "phy ce29 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce29 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce29 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce29 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce29 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x10;phy ce29 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x20;phy ce29 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x3C;phy ce29 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xC AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce29 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce29 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce29 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce29 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce29 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x10;phy ce29 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x20;phy ce29 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x3C;phy ce29 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xC AMS_TX_DRIVERMODE=0x0" + ;; + 30) + bcmcmd "phy ce30 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce30 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce30 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce30 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce30 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x10;phy ce30 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x24;phy ce30 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x3C;phy ce30 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xC AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce30 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce30 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce30 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce30 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce30 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x8;phy ce30 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x24;phy ce30 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x3C;phy ce30 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xC AMS_TX_DRIVERMODE=0x0" + ;; + 31) + bcmcmd "phy ce31 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_PRE=0x1F;phy ce31 CL93N72_UT_CTL2r.0 CL93N72_TXFIR_POST=0x15;phy ce31 CL93N72_UT_CTL3r.0 CL93N72_TXFIR_MAIN=0x3C;phy ce31 AMS_TX_CTL2r.0 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce31 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_PRE=0x10;phy ce31 CL93N72_UT_CTL2r.1 CL93N72_TXFIR_POST=0x24;phy ce31 CL93N72_UT_CTL3r.1 CL93N72_TXFIR_MAIN=0x3C;phy ce31 AMS_TX_CTL2r.1 AMS_TX_AMP_CTL=0xC AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce31 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_PRE=0x1F;phy ce31 CL93N72_UT_CTL2r.2 CL93N72_TXFIR_POST=0x15;phy ce31 CL93N72_UT_CTL3r.2 CL93N72_TXFIR_MAIN=0x3C;phy ce31 AMS_TX_CTL2r.2 AMS_TX_AMP_CTL=0xF AMS_TX_DRIVERMODE=0x0" + bcmcmd "phy ce31 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_PRE=0x14;phy ce31 CL93N72_UT_CTL2r.3 CL93N72_TXFIR_POST=0x20;phy ce31 CL93N72_UT_CTL3r.3 CL93N72_TXFIR_MAIN=0x3C;phy ce31 AMS_TX_CTL2r.3 AMS_TX_AMP_CTL=0xE AMS_TX_DRIVERMODE=0x0" + ;; + *) + echo "Unknown Port" + return + ;; + esac +} + +# Help usage function +function _help { + echo "=========================================================" + echo "# Description: Help Function" + echo "=========================================================" + echo "----------------------------------------------------" + echo "EX : ${0} help" + echo " : ${0} optical [0-31]" + echo " : ${0} dac [0-31]" + echo "----------------------------------------------------" +} + +#Main Function +function _main { + + if [ "${TYPE}" == "help" ]; then + _help + elif [ "${TYPE}" == "optical" ]; then + _qsfp_optical_si_set + elif [ "${TYPE}" == "dac" ]; then + _qsfp_dac_si_set + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi +} + +_main diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9100/utils/s9100_monitor.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/utils/s9100_monitor.sh new file mode 100755 index 000000000000..974da6d5001f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9100/utils/s9100_monitor.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=5 +I2C_UTILS="/usr/sbin/i2c_utils.sh" + + +# LED status monitor +function _led_monitor { + ${I2C_UTILS} i2c_led_fan_status_set >/dev/null + ${I2C_UTILS} i2c_led_psu_status_set >/dev/null + ${I2C_UTILS} i2c_led_fan_tray_status_set >/dev/null +} + +# main function +function _main { + while true + do + _led_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/README.md b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/README.md new file mode 100644 index 000000000000..940d15f497c9 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/README.md @@ -0,0 +1,184 @@ +# Ingrasys S9200-64X Platform Driver for SONiC + +Copyright (C) 2016 Ingrasys, Inc. + +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 3 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, see . + + +## Licensing terms + +The Licensing terms of the files within this project is split 2 parts. +The Linux kernel is released under the GNU General Public License version 2. +All other code is released under the GNU General Public License version 3. +Please see the LICENSE file for copies of both licenses. + +## Contents of this package + + - service/ + > Service config files for platform initialization and monitoring + - utils/ + > Scripts useful during platform bringup and sysfs function + - conf/ + > Platform configure files. + +## Kernel modules and drivers + +The driver for interacting with Ingrasys S9200-64X is contained in the I2C +kernel module and initialization script. The initialization script loads +the modules in the correct order. It has been built and tested against +the Linux 3.16 kernel. + +The initialization script will modprobe the needed modules, navigate to the +module's device directory in sysfs, and write configuration data to +the kernel module. + +### IGB + +This is OOB Ports on front panel for management plane. + +The IGB module must be loaded first on Ingrasys S9200-64X platform. + +### I2C i801 + +The I2C i801 on Ingrasys S9200-64X can be found in +`/sys/bus/i2c/devices/i2c-0/` + +This is I2C bus for Clock Gen, DIMM channel and digital potentiometers. + +The i801 module must be loaded second on Ingrasys S9200-64X. + +### I2C iSMT + +The I2C iSMT module on S9200-64X can be found in +`/sys/bus/i2c/devices/i2c-1/` + +This is I2C bus for CPLD, HWM, power controller and I2C Switches. + +The i801 module must be loaded third on Ingrasys S9200-64X. + +### I2C PCA9548 +The PCA9548 module on S9200-64X can be found in +`/sys/bus/i2c/devices/i2c-2/` , `/sys/bus/i2c/devices/i2c-3/`, +`/sys/bus/i2c/devices/i2c-4/`, `/sys/bus/i2c/devices/i2c-5/`, +`/sys/bus/i2c/devices/i2c-6/`, `/sys/bus/i2c/devices/i2c-7/`, +`/sys/bus/i2c/devices/i2c-8/`, `/sys/bus/i2c/devices/i2c-9/`. + +The pca9548 module for zQSFP module get/set functions, PSU information, +fan status and EEPROM. + +## Hardware components + +The hardware components are initialized in the init script on S9200-64X. +The following describes manual initialization as well as interaction. +The examples below are just for Ingrasys S9200-64X platform. + +### Hardware initialization + +When the sonic-platform-ingrasys-s9200-64x package is installed on the S9200-64X, +it is automatically initialized. If you want to manual initialization, the +utility command usage as follows: +``` + i2c_utils.sh i2c_init +``` + +### EEPROM + +The EEPROM is including the board SKU, model name, vendor name, serial number, +and other information can be accessed with the specific eeprom kernel module. +After using `modprobe eeprom_mb` to detect the eeprom, it will show up in sysfs. + +The hexdump utility can be used to decode the raw output of the EEPROM. +For example, +``` + bash# echo "mb_eeprom 0x54" > /sys/bus/i2c/devices/i2c-9/new_device + bash# cat /sys/bus/i2c/drivers/mb_eeprom/9-0054/eeprom | hexdump -C +``` + +### Front panel LEDs + +LEDs can be setup on/off by using i2c utility `/usr/sbin/i2c_utils.sh`. +Utility function command usage as follows: + +``` +Status LED: + i2c_utils.sh i2c_sys_led green|amber + +Fan status LED: + i2c_utils.sh i2c_fan_led green|amber + +PSU1 status LED: + i2c_utils.sh i2c_psu1_led green|amber + +PSU2 status LED: + i2c_utils.sh i2c_psu2_led green|amber + +``` +QSFP Module Port LEDs control by ASIC library. + + +### Fan speed + +Fan speed are controlled by the w83795 kernel module. +It can be found in `/sys/class/hwmon/hwmon5/device/`. +If they were compiled as modules, you will need to modprobe w83795 for +their sysfs entries to show up. Each fan has an `fan_input` file +for reading the fan speed. And `pwm2` setting fan1/fan3/fan5/fan7. + +There is docker-platform-monitor container installed fancontrol package that can +automatic control platform fan speed. + + +### Temperature sensors + +Temperature sensors are controlled by the w83795 kernel +module. It can be found in `/sys/class/hwmon/hwmon5/device/`. +If they were compiled as modules, then you will need to modprobe w83795 for +their sysfs entries to show up. +`temp1_input` is front MAC temperature sensor. `temp2_input` is rear MAC +temperature sensor. + +There is docker-platform-monitor container installed lm-sensors package that can +monitor platform temperature. And `sensors` command can show each +temperature sensors status. + +### Power supplies + +Power supplies status and its EEPROM info can be used i2c utility +`/usr/sbin/i2c_utils.sh` to get. +The usage as follows: +``` +PSU EEPROM: + i2c_utils.sh i2c_psu_eeprom_get + hexdump -C psu0.rom + hexdump -C psu1.rom + +PSU Status: + i2c_utils.sh i2c_psu_status +``` + +### QSFPs +QSFP modules are managed by the pca9548 kernel driver. +The i2c utility `/usr/sbin/i2c_utils.sh` can be used to get status and +module EEPROM informations. +The usage as follows: +``` +QSFP EEPROM: + i2c_utils.sh i2c_qsfp_eeprom_get [1-64] + +QSFP Insert Event: + i2c_utils.sh i2c_qsfp_eeprom_get [1-64] + 0 => not insert + 1 => inserted +``` + diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/Makefile b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/Makefile new file mode 100755 index 000000000000..1a9e4e314e8e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/Makefile @@ -0,0 +1,3 @@ +obj-m := eeprom_mb.o +obj-m += cpld.o +obj-m += ingrasys_s9200_64x_psu.o diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/cpld.c b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/cpld.c new file mode 100755 index 000000000000..7b16f5d3e321 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/cpld.c @@ -0,0 +1,498 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define S9200_MUX_BASE_NR 0 +#define CPLD_DEVICE_NUM 5 + +/* CPLD registers */ +#define CPLD_REG_REV 0x01 +#define CPLD_REG_ID 0x02 +#define CPLD_REG_10G_MUX 0x41 + +/* QSFP signal bit in register */ +#define BIT_RST 0 +#define BIT_LPM 2 +#define BIT_INT 0 +#define BIT_ABS 1 + +static void device_release(struct device *dev) +{ + return; +} + +/* + * S9200 CPLD register addresses + */ +static const int int_abs_reg[CPLD_DEVICE_NUM][2]= { + {0x20, 0x2B}, + {0x20, 0x2C}, + {0x20, 0x2C}, + {0x20, 0x2C}, + {0x20, 0x2C} +}; + +static const int rst_lp_reg[CPLD_DEVICE_NUM][2]= { + {0x30, 0x3B}, + {0x30, 0x3C}, + {0x30, 0x3C}, + {0x30, 0x3C}, + {0x30, 0x3C} +}; + +/* + * S9200 CPLD + */ + +enum cpld_type { + cpld_1, + cpld_2, + cpld_3, + cpld_4, + cpld_5, +}; + +enum qsfp_signal { + sig_int, + sig_abs, + sig_rst, + sig_lpm +}; + +struct cpld_platform_data { + int reg_addr; + struct i2c_client *client; +}; + +static struct cpld_platform_data s9200_cpld_platform_data[] = { + [cpld_1] = { + .reg_addr = 0x33, + }, + + [cpld_2] = { + .reg_addr = 0x33, + }, + + [cpld_3] = { + .reg_addr = 0x33, + }, + + [cpld_4] = { + .reg_addr = 0x33, + }, + + [cpld_5] = { + .reg_addr = 0x33, + }, +}; + +static struct platform_device s9200_cpld = { + .name = "ingrasys-s9200-cpld", + .id = 0, + .dev = { + .platform_data = s9200_cpld_platform_data, + .release = device_release + }, +}; + +/* + * S9200 I2C DEVICES + */ + +struct i2c_device_platform_data { + int parent; + struct i2c_board_info info; + struct i2c_client *client; +}; + +/* module_platform_driver */ +static ssize_t get_cpld_reg(struct device *dev, struct device_attribute *devattr, char *buf, int signal) +{ + int ret; + u64 data = 0; + u64 shift = 0; + int i = 0; + int j = 0; + int port = 0; + int bit = 0; + int bit_mask = 0; + int (*reg)[CPLD_DEVICE_NUM][2]; + struct cpld_platform_data *pdata = NULL; + + pdata = dev->platform_data; + + switch(signal) { + case sig_int: + bit = BIT_INT; + reg = (typeof(reg)) &int_abs_reg; + break; + case sig_abs: + bit = BIT_ABS; + reg = (typeof(reg)) &int_abs_reg; + break; + case sig_rst: + bit = BIT_RST; + reg = (typeof(reg)) &rst_lp_reg; + break; + case sig_lpm: + bit = BIT_LPM; + reg = (typeof(reg)) &rst_lp_reg; + break; + default: + return sprintf(buf, "na"); + } + bit_mask = 0x1 << bit; + + for (i=0; i> bit)) << port; + data |= shift; + port++; + } + } + + return sprintf(buf, "0x%016llx\n", data); +} + +static ssize_t set_cpld_reg(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count, int signal) +{ + unsigned long data; + int err; + struct cpld_platform_data *pdata = dev->platform_data; + u8 current_reg_val = 0; + u8 new_reg_val = 0; + int value; + int i = 0; + int j = 0; + int port = 0; + int ret = 0; + int bit = 0; + int (*reg)[CPLD_DEVICE_NUM][2]; + + err = kstrtoul(buf, 16, &data); + if (err) + return err; + + switch(signal) { + case sig_int: + bit = BIT_INT; + reg = (typeof(reg)) &int_abs_reg; + break; + case sig_abs: + bit = BIT_ABS; + reg = (typeof(reg)) &int_abs_reg; + break; + case sig_rst: + bit = BIT_RST; + reg = (typeof(reg)) &rst_lp_reg; + break; + case sig_lpm: + bit = BIT_LPM; + reg = (typeof(reg)) &rst_lp_reg; + break; + default: + return sprintf(buf, "na"); + } + + for (i=0; i> port) & 0x1; + + //set value on bit N of new_reg_val + if (value > 0) { + new_reg_val = current_reg_val | (u8) (0x1 << bit); + } else { + new_reg_val = current_reg_val & (u8) ~(0x1 << bit); + } + //write reg value if changed + if (current_reg_val != new_reg_val) { + ret = i2c_smbus_write_byte_data(pdata[i].client, j, + (u8)(new_reg_val)); + if (ret < 0){ + return ret; + } + } + port++; + } + } + + return count; +} + +static ssize_t get_lpmode(struct device *dev, struct device_attribute *devattr, char *buf) +{ + return get_cpld_reg(dev, devattr, buf, sig_lpm); +} + +static ssize_t set_lpmode(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + return set_cpld_reg(dev, devattr, buf, count, sig_lpm); +} + +static ssize_t get_reset(struct device *dev, struct device_attribute *devattr, char *buf) +{ + return get_cpld_reg(dev, devattr, buf, sig_rst); +} + +static ssize_t set_reset(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + return set_cpld_reg(dev, devattr, buf, count, sig_rst); +} + +static ssize_t get_modprs(struct device *dev, struct device_attribute *devattr, char *buf) +{ + return get_cpld_reg(dev, devattr, buf, sig_abs); +} + +static ssize_t get_int(struct device *dev, struct device_attribute *devattr, char *buf) +{ + return get_cpld_reg(dev, devattr, buf, sig_int); +} + +static ssize_t get_cpld_version(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int i = 0; + int cnt = 0; + u8 reg_val_rev[CPLD_DEVICE_NUM]; + u8 reg_val_id[CPLD_DEVICE_NUM]; + + struct cpld_platform_data *pdata = dev->platform_data; + + //get reg value + for (i=0; i> 6 & 0x1, + reg_val_id[i] & 0x7); + } + + return cnt; +} + +static ssize_t set_10g_mux(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + u8 data; + u8 current_reg_val = 0; + u8 new_reg_val = 0; + int cpld_dev_num=cpld_1; + int reg_offset=CPLD_REG_10G_MUX; + int err; + struct cpld_platform_data *pdata = dev->platform_data; + int value; + int i = 0; + int port = 0; + int ret = 0; + int bit = 0; + + err = kstrtou8(buf, 16, &new_reg_val); + if (err) + return err; + + //read reg value + current_reg_val = i2c_smbus_read_byte_data(pdata[cpld_dev_num].client, reg_offset); + if (current_reg_val < 0) { + return current_reg_val; + } + + //write reg value if changed + if (current_reg_val != new_reg_val) { + ret = i2c_smbus_write_byte_data(pdata[cpld_dev_num].client, reg_offset, + (u8)(new_reg_val)); + if (ret < 0){ + return ret; + } + } + + return count; +} + +static ssize_t get_10g_mux(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int cpld_dev_num=cpld_1; + int reg_offset=CPLD_REG_10G_MUX; + u8 reg_val=0; + struct cpld_platform_data *pdata = dev->platform_data; + + //read 10G mux register + reg_val = i2c_smbus_read_byte_data(pdata[cpld_dev_num].client, reg_offset); + if (reg_val < 0) { + return sprintf(buf, "na"); + } + + return sprintf(buf, "0x%x\n", reg_val); +} + +static DEVICE_ATTR(qsfp_int, S_IRUGO, get_int, NULL); +static DEVICE_ATTR(qsfp_modprs, S_IRUGO, get_modprs, NULL); +static DEVICE_ATTR(qsfp_lpmode, S_IRUGO | S_IWUSR, get_lpmode, set_lpmode); +static DEVICE_ATTR(qsfp_reset, S_IRUGO | S_IWUSR, get_reset, set_reset); +static DEVICE_ATTR(cpld_version, S_IRUGO, get_cpld_version, NULL); +static DEVICE_ATTR(cpld_10g_mux, S_IRUGO | S_IWUSR, get_10g_mux, set_10g_mux); + +static struct attribute *s9200_cpld_attrs[] = { + &dev_attr_qsfp_int.attr, + &dev_attr_qsfp_lpmode.attr, + &dev_attr_qsfp_reset.attr, + &dev_attr_qsfp_modprs.attr, + &dev_attr_cpld_version.attr, + &dev_attr_cpld_10g_mux.attr, + NULL, +}; + +static struct attribute_group s9200_cpld_attr_grp = { + .attrs = s9200_cpld_attrs, +}; + +static int __init cpld_probe(struct platform_device *pdev) +{ + struct cpld_platform_data *pdata; + struct i2c_adapter *parent[CPLD_DEVICE_NUM]; + int i; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -ENODEV; + } + + for (i = 0; i < CPLD_DEVICE_NUM; i++) { + parent[i] = i2c_get_adapter(S9200_MUX_BASE_NR + i + 1); + if (!parent[i]) { + printk(KERN_WARNING "Parent adapter (%d) not found\n", + S9200_MUX_BASE_NR + i + 1); + return -ENODEV; + } + pdata[i].client = i2c_new_dummy(parent[i], pdata[i].reg_addr); + if (!pdata[i].client) { + printk(KERN_WARNING "Fail to create dummy i2c client for addr %d\n", pdata[i].reg_addr); + goto error; + } + } + + ret = sysfs_create_group(&pdev->dev.kobj, &s9200_cpld_attr_grp); + if (ret) + goto error; + + return 0; + +error: + i2c_put_adapter(parent[i]); + i--; + for (; i >= 0; i--) { + if (pdata[i].client) { + i2c_unregister_device(pdata[i].client); + i2c_put_adapter(parent[i]); + } + } + + return -ENODEV; +} + +static int __exit cpld_remove(struct platform_device *pdev) +{ + int i; + struct i2c_adapter *parent = NULL; + struct cpld_platform_data *pdata = pdev->dev.platform_data; + + sysfs_remove_group(&pdev->dev.kobj, &s9200_cpld_attr_grp); + + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + } else { + for (i = 0; i < CPLD_DEVICE_NUM; i++) { + if (pdata[i].client) { + parent = (pdata[i].client)->adapter; + i2c_unregister_device(pdata[i].client); + i2c_put_adapter(parent); + } + } + } + + return 0; +} + +static struct platform_driver cpld_driver = { + .probe = cpld_probe, + .remove = __exit_p(cpld_remove), + .driver = { + .owner = THIS_MODULE, + .name = "ingrasys-s9200-cpld", + }, +}; + +static int __init ingrasys_s9200_platform_init(void) +{ + int ret = 0; + + printk("ingrasysl_s9200_platform module initialization\n"); + + mdelay(10000); + + ret = platform_driver_register(&cpld_driver); + if (ret) { + printk(KERN_WARNING "Fail to register cpld driver\n"); + goto error_cpld_driver; + } + ret = platform_device_register(&s9200_cpld); + if (ret) { + printk(KERN_WARNING "Fail to create cpld device\n"); + goto error_cpld; + } + + return 0; + +error_cpld: + platform_driver_unregister(&cpld_driver); +error_cpld_driver: + return ret; +} + +static void __exit ingrasys_s9200_platform_exit(void) +{ + platform_device_unregister(&s9200_cpld); + platform_driver_unregister(&cpld_driver); +} + +module_init(ingrasys_s9200_platform_init); +module_exit(ingrasys_s9200_platform_exit); + +MODULE_DESCRIPTION("Ingrasys S9200 Platform Support"); +MODULE_AUTHOR("Jason Tsai "); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/eeprom_mb.c b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/eeprom_mb.c new file mode 100755 index 000000000000..1cdb26658f5a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/eeprom_mb.c @@ -0,0 +1,267 @@ +/* + * Copyright (C) 1998, 1999 Frodo Looijaard and + * Philip Edelbrock + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003 IBM Corp. + * Copyright (C) 2004 Jean Delvare + * + * 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. + */ + +/* enable dev_dbg print out */ +//#define DEBUG + +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { /*0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57,*/ I2C_CLIENT_END }; + +/* Size of EEPROM in bytes */ +#define EEPROM_SIZE 512 + +#define SLICE_BITS (6) +#define SLICE_SIZE (1 << SLICE_BITS) +#define SLICE_NUM (EEPROM_SIZE/SLICE_SIZE) + +/* Each client has this additional data */ +struct eeprom_data { + struct mutex update_lock; + u8 valid; /* bitfield, bit!=0 if slice is valid */ + unsigned long last_updated[SLICE_NUM]; /* In jiffies, 8 slices */ + u8 data[EEPROM_SIZE]; /* Register values */ +}; + + +static void mb_eeprom_update_client(struct i2c_client *client, u8 slice) +{ + struct eeprom_data *data = i2c_get_clientdata(client); + int i, j; + int ret; + int addr; + + mutex_lock(&data->update_lock); + + if (!(data->valid & (1 << slice)) || + time_after(jiffies, data->last_updated[slice] + 300 * HZ)) { + dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice); + + addr = slice << SLICE_BITS; + + ret = i2c_smbus_write_byte_data(client, (u8)((addr >> 8) & 0xFF), (u8)(addr & 0xFF)); + /* select the eeprom address */ + if (ret < 0) { + dev_err(&client->dev, "address set failed\n"); + goto exit; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE)) { + goto exit; + } + + for (i = slice << SLICE_BITS; i < (slice + 1) << SLICE_BITS; i+= SLICE_SIZE) { + for (j = i; j < (i+SLICE_SIZE); j++) { + int res; + + res = i2c_smbus_read_byte(client); + if (res < 0) { + goto exit; + } + + data->data[j] = res & 0xFF; + } + } + + data->last_updated[slice] = jiffies; + data->valid |= (1 << slice); + } +exit: + mutex_unlock(&data->update_lock); +} + +static ssize_t mb_eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct eeprom_data *data = i2c_get_clientdata(client); + u8 slice; + + if (off > EEPROM_SIZE) { + return 0; + } + if (off + count > EEPROM_SIZE) { + count = EEPROM_SIZE - off; + } + if (count == 0) { + return 0; + } + + /* Only refresh slices which contain requested bytes */ + for (slice = off >> SLICE_BITS; slice <= (off + count - 1) >> SLICE_BITS; slice++) { + mb_eeprom_update_client(client, slice); + } + + memcpy(buf, &data->data[off], count); + + return count; +} + +static ssize_t mb_eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct eeprom_data *data = i2c_get_clientdata(client); + int ret; + int i; + u8 cmd; + u16 value16; + + dev_dbg(&client->dev, "mb_eeprom_write off=%d, count=%d\n", (int)off, (int)count); + + if (off > EEPROM_SIZE) { + return 0; + } + if (off + count > EEPROM_SIZE) { + count = EEPROM_SIZE - off; + } + if (count == 0) { + return 0; + } + + mutex_lock(&data->update_lock); + + for(i=0; i < count; i++) { + /* write command */ + cmd = (off >> 8) & 0xff; + value16 = off & 0xff; + value16 |= buf[i] << 8; + ret = i2c_smbus_write_word_data(client, cmd, value16); + + if (ret < 0) { + dev_err(&client->dev, "write address failed at %d \n", (int)off); + goto exit; + } + + off++; + + /* need to wait for write complete */ + udelay(10000); + } +exit: + mutex_unlock(&data->update_lock); + /* force to update client when reading */ + for(i=0; i < SLICE_NUM; i++) { + data->last_updated[i] = 0; + } + + return count; +} + +static struct bin_attribute mb_eeprom_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO | S_IWUSR, + }, + .size = EEPROM_SIZE, + .read = mb_eeprom_read, + .write = mb_eeprom_write, +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int mb_eeprom_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all + addresses 0x50-0x57, but we only care about 0x51 and 0x55. So decline + attaching to addresses >= 0x56 on DDC buses */ + if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x56) { + return -ENODEV; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE) + && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + return -ENODEV; + } + + strlcpy(info->type, "eeprom", I2C_NAME_SIZE); + + return 0; +} + +static int mb_eeprom_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct eeprom_data *data; + int err; + + if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + memset(data->data, 0xff, EEPROM_SIZE); + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &mb_eeprom_attr); + if (err) { + goto exit_kfree; + } + + return 0; + +exit_kfree: + kfree(data); +exit: + return err; +} + +static int mb_eeprom_remove(struct i2c_client *client) +{ + sysfs_remove_bin_file(&client->dev.kobj, &mb_eeprom_attr); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id mb_eeprom_id[] = { + { "mb_eeprom", 0 }, + { } +}; + +static struct i2c_driver mb_eeprom_driver = { + .driver = { + .name = "mb_eeprom", + }, + .probe = mb_eeprom_probe, + .remove = mb_eeprom_remove, + .id_table = mb_eeprom_id, + + .class = I2C_CLASS_DDC | I2C_CLASS_SPD, + .detect = mb_eeprom_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(mb_eeprom_driver); + +MODULE_AUTHOR("Wade "); +MODULE_DESCRIPTION("Ingrasys Mother Board EEPROM driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/ingrasys_s9200_64x_platform.h b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/ingrasys_s9200_64x_platform.h new file mode 100644 index 000000000000..8a38965e7932 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/ingrasys_s9200_64x_platform.h @@ -0,0 +1,148 @@ +#ifndef _S9200_64X_PLATFORM_H +#define _S9200_64X_PLATFORM_H + +#include + +// remove debug before release +#define DEBUG + +enum bus_order { + I2C_BUS_MAIN, + MUX_9548_0_CH0, + MUX_9548_0_CH1, + MUX_9548_0_CH2, + MUX_9548_0_CH3, + MUX_9548_0_CH4, + MUX_9548_0_CH5, + MUX_9548_0_CH6, + MUX_9548_0_CH7, + MUX_9548_1_CH0, + MUX_9548_1_CH1, + MUX_9548_1_CH2, + MUX_9548_1_CH3, + MUX_9548_1_CH4, + MUX_9548_1_CH5, + MUX_9548_1_CH6, + MUX_9548_1_CH7, + MUX_9546_0_CH0, + MUX_9546_0_CH1, + MUX_9546_0_CH2, + MUX_9546_0_CH3, + MUX_9546_1_CH0, + MUX_9546_1_CH1, + MUX_9546_1_CH2, + MUX_9546_1_CH3, + MUX_9548_2_CH0, + MUX_9548_2_CH1, + MUX_9548_2_CH2, + MUX_9548_2_CH3, + MUX_9548_2_CH4, + MUX_9548_2_CH5, + MUX_9548_2_CH6, + MUX_9548_2_CH7, + MUX_9548_3_CH0, + MUX_9548_3_CH1, + MUX_9548_3_CH2, + MUX_9548_3_CH3, + MUX_9548_3_CH4, + MUX_9548_3_CH5, + MUX_9548_3_CH6, + MUX_9548_3_CH7, + MUX_9548_4_CH0, + MUX_9548_4_CH1, + MUX_9548_4_CH2, + MUX_9548_4_CH3, + MUX_9548_4_CH4, + MUX_9548_4_CH5, + MUX_9548_4_CH6, + MUX_9548_4_CH7, + MUX_9548_5_CH0, + MUX_9548_5_CH1, + MUX_9548_5_CH2, + MUX_9548_5_CH3, + MUX_9548_5_CH4, + MUX_9548_5_CH5, + MUX_9548_5_CH6, + MUX_9548_5_CH7, + MUX_9548_6_CH0, + MUX_9548_6_CH1, + MUX_9548_6_CH2, + MUX_9548_6_CH3, + MUX_9548_6_CH4, + MUX_9548_6_CH5, + MUX_9548_6_CH6, + MUX_9548_6_CH7, + MUX_9548_7_CH0, + MUX_9548_7_CH1, + MUX_9548_7_CH2, + MUX_9548_7_CH3, + MUX_9548_7_CH4, + MUX_9548_7_CH5, + MUX_9548_7_CH6, + MUX_9548_7_CH7, + MUX_9548_8_CH0, + MUX_9548_8_CH1, + MUX_9548_8_CH2, + MUX_9548_8_CH3, + MUX_9548_8_CH4, + MUX_9548_8_CH5, + MUX_9548_8_CH6, + MUX_9548_8_CH7, + MUX_9548_9_CH0, + MUX_9548_9_CH1, + MUX_9548_9_CH2, + MUX_9548_9_CH3, + MUX_9548_9_CH4, + MUX_9548_9_CH5, + MUX_9548_9_CH6, + MUX_9548_9_CH7, + MUX_9548_10_CH0, + MUX_9548_10_CH1, + MUX_9548_10_CH2, + MUX_9548_10_CH3, + MUX_9548_10_CH4, + MUX_9548_10_CH5, + MUX_9548_10_CH6, + MUX_9548_10_CH7, +}; + +#define I2C_ADDR_MUX_9555_0 (0x20) +#define I2C_ADDR_MUX_9555_1 (0x24) +#define I2C_ADDR_MUX_9555_2 (0x25) +#define I2C_ADDR_MUX_9555_3 (0x26) +#define I2C_ADDR_MUX_9539_0 (0x76) +#define I2C_ADDR_MUX_9539_1 (0x76) +#define I2C_BUS_FAN_STATUS (I2C_BUS_MAIN) +#define I2C_BUS_SYS_LED (MUX_9548_1_CH1) + +#define NUM_OF_I2C_MUX (11) +#define NUM_OF_CPLD (5) +#define NUM_OF_QSFP_PORT (64) +#define NUM_OF_SFP_PORT (2) +#define QSFP_EEPROM_I2C_ADDR (0x50) + +enum gpio_reg { + REG_PORT0_IN, + REG_PORT1_IN, + REG_PORT0_OUT, + REG_PORT1_OUT, + REG_PORT0_POL, + REG_PORT1_POL, + REG_PORT0_DIR, + REG_PORT1_DIR, +}; + +struct ing_i2c_board_info { + int ch; + int size; + struct i2c_board_info *board_info; +}; + +struct i2c_init_data { + __u16 ch; + __u16 addr; + __u8 reg; + __u8 value; +}; + +#endif diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/ingrasys_s9200_64x_psu.c b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/ingrasys_s9200_64x_psu.c new file mode 100644 index 000000000000..46642f1271ca --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/modules/ingrasys_s9200_64x_psu.c @@ -0,0 +1,389 @@ +/* + * S9200-64X PSU driver + * + * Copyright (C) 2017 Ingrasys, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ingrasys_s9200_64x_platform.h" + +static ssize_t show_psu_eeprom(struct device *dev, + struct device_attribute *da, + char *buf); +static struct s9200_psu_data *s9200_psu_update_status(struct device *dev); +static struct s9200_psu_data *s9200_psu_update_eeprom(struct device *dev); +static int s9200_psu_read_block(struct i2c_client *client, + u8 command, + u8 *data, + int data_len); + + +#define DRIVER_NAME "psu" + +// Addresses scanned +static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; + +/* PSU EEPROM SIZE */ +#define EEPROM_SZ 256 +#define READ_EEPROM 1 +#define NREAD_EEPROM 0 + +static struct i2c_client pca9555_client; + +/* pca9555 gpio pin mapping */ +#define PSU2_PWROK 0 +#define PSU2_PRSNT_L 1 +#define PSU2_PWRON_L 2 +#define PSU1_PWROK 3 +#define PSU1_PRSNT_L 4 +#define PSU1_PWRON_L 5 +#define TMP_75_INT_L 6 + +/* Driver Private Data */ +struct s9200_psu_data { + struct mutex lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + s32 status; /* IO expander value */ + char eeprom[EEPROM_SZ]; /* psu eeprom data */ + char psuABS; /* PSU absent */ + char psuPG; /* PSU power good */ +}; + +enum psu_index +{ + s9200_psu1, + s9200_psu2 +}; + +/* + * display power good attribute + */ +static ssize_t +show_psu_pg(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct s9200_psu_data *data = s9200_psu_update_status(dev); + unsigned int value; + + mutex_lock(&data->lock); + value = data->psuPG; + mutex_unlock(&data->lock); + + return sprintf(buf, "%d\n", value); +} + +/* + * display power absent attribute + */ +static ssize_t +show_psu_abs(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct s9200_psu_data *data = s9200_psu_update_status(dev); + unsigned int value; + + mutex_lock(&data->lock); + value = data->psuABS; + mutex_unlock(&data->lock); + + return sprintf(buf, "%d\n", value); +} + + +/* + * sysfs attributes for psu + */ +static DEVICE_ATTR(psu_pg, S_IRUGO, show_psu_pg, NULL); +static DEVICE_ATTR(psu_abs, S_IRUGO, show_psu_abs, NULL); +static DEVICE_ATTR(psu_eeprom, S_IRUGO, show_psu_eeprom, NULL); + +static struct attribute *s9200_psu_attributes[] = { + &dev_attr_psu_pg.attr, + &dev_attr_psu_abs.attr, + &dev_attr_psu_eeprom.attr, + NULL +}; + +/* + * display psu eeprom content + */ +static ssize_t +show_psu_eeprom(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct s9200_psu_data *data = s9200_psu_update_eeprom(dev); + + memcpy(buf, (char *)data->eeprom, EEPROM_SZ); + return EEPROM_SZ; +} + +static const struct attribute_group s9200_psu_group = { + .attrs = s9200_psu_attributes, +}; + +/* + * check gpio expander is accessible + */ +static int +pca9555_detect(struct i2c_client *client) +{ + if (i2c_smbus_read_byte_data(client, REG_PORT0_DIR) < 0) { + return -ENODEV; + } + + return 0; +} + +/* + * client address init + */ +static void +i2c_devices_client_address_init(struct i2c_client *client) +{ + pca9555_client = *client; + pca9555_client.addr = 0x25; +} + +static int +s9200_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct s9200_psu_data *data; + int status, err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct s9200_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct s9200_psu_data)); + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->lock); + + i2c_devices_client_address_init(client); + + err = pca9555_detect(&pca9555_client); + if (err) { + dev_info(&client->dev, "pca9555_detect failed\n"); + return err; + } + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &s9200_psu_group); + if (status) { + goto exit_free; + } + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &s9200_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int +s9200_psu_remove(struct i2c_client *client) +{ + struct s9200_psu_data *data = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &s9200_psu_group); + kfree(data); + + return 0; +} + + +/* + * psu eeprom read utility + */ +static int +s9200_psu_read_block(struct i2c_client *client, + u8 command, + u8 *data, + int data_len) +{ + int i=0, ret=0; + int blk_max = 32; //max block read size + + /* read eeprom, 32 * 8 = 256 bytes */ + for (i=0; i < EEPROM_SZ/blk_max; i++) { + ret = i2c_smbus_read_i2c_block_data(client, (i*blk_max), blk_max, + data + (i*blk_max)); + if (ret < 0) { + return ret; + } + } + return ret; +} + +/* + * update eeprom content + */ +static struct s9200_psu_data +*s9200_psu_update_eeprom(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s9200_psu_data *data = i2c_get_clientdata(client); + s32 status = 0; + int psu_pwrok = 0; + int psu_prsnt_l = 0; + + mutex_lock(&data->lock); + + if (time_after(jiffies, data->last_updated + 300 * HZ) + || !data->valid) { + + /* Read psu status */ + + status = i2c_smbus_read_word_data(&(pca9555_client), REG_PORT0_IN); + data->status = status; + + /*read psu status from io expander*/ + + if (data->index == s9200_psu1) { + psu_pwrok = PSU1_PWROK; + psu_prsnt_l = PSU1_PRSNT_L; + } else { + psu_pwrok = PSU2_PWROK; + psu_prsnt_l = PSU2_PRSNT_L; + } + data->psuPG = (status >> psu_pwrok) & 0x1; + data->psuABS = (status >> psu_prsnt_l) & 0x1; + + /* Read eeprom */ + if (!data->psuABS) { + //clear local eeprom data + memset(data->eeprom, 0, EEPROM_SZ); + + //read eeprom + status = s9200_psu_read_block(client, 0, data->eeprom, + ARRAY_SIZE(data->eeprom)); + + if (status < 0) { + memset(data->eeprom, 0, EEPROM_SZ); + dev_err(&client->dev, "Read eeprom failed, status=(%d)\n", status); + } else { + data->valid = 1; + } + } else { + memset(data->eeprom, 0, EEPROM_SZ); + } + data->last_updated = jiffies; + } + + mutex_unlock(&data->lock); + + return data; +} + +/* + * update psu status + */ +static struct s9200_psu_data +*s9200_psu_update_status(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s9200_psu_data *data = i2c_get_clientdata(client); + s32 status = 0; + int psu_pwrok = 0; + int psu_prsnt_l = 0; + + mutex_lock(&data->lock); + + /* Read psu status */ + + status = i2c_smbus_read_word_data(&(pca9555_client), REG_PORT0_IN); + data->status = status; + + /*read psu status from io expander*/ + + if (data->index == s9200_psu1) { + psu_pwrok = PSU1_PWROK; + psu_prsnt_l = PSU1_PRSNT_L; + } else { + psu_pwrok = PSU2_PWROK; + psu_prsnt_l = PSU2_PRSNT_L; + } + data->psuPG = (status >> psu_pwrok) & 0x1; + data->psuABS = (status >> psu_prsnt_l) & 0x1; + + mutex_unlock(&data->lock); + + return data; +} + +static const struct i2c_device_id s9200_psu_id[] = { + { "psu1", s9200_psu1 }, + { "psu2", s9200_psu2 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, s9200_psu_id); + +static struct i2c_driver s9200_psu_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = s9200_psu_probe, + .remove = s9200_psu_remove, + .id_table = s9200_psu_id, + .address_list = normal_i2c, +}; + +static int __init s9200_psu_init(void) +{ + return i2c_add_driver(&s9200_psu_driver); +} + +static void __exit s9200_psu_exit(void) +{ + i2c_del_driver(&s9200_psu_driver); +} + +module_init(s9200_psu_init); +module_exit(s9200_psu_exit); + +MODULE_AUTHOR("Jason Tsai "); +MODULE_DESCRIPTION("S9200-64X psu driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/service/qsfp-monitor.service b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/service/qsfp-monitor.service new file mode 100644 index 000000000000..bcb9beb6a999 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/service/qsfp-monitor.service @@ -0,0 +1,15 @@ +[Unit] +Description= This QSFP Monitor service is to setup QSFP SI. +Requires=s9200-64x-monitor.service +After=s9200-64x-monitor.service + +[Service] +ExecStart=/usr/sbin/qsfp_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/service/s9200-64x-monitor.service b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/service/s9200-64x-monitor.service new file mode 100644 index 000000000000..bd92628a6c3f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/service/s9200-64x-monitor.service @@ -0,0 +1,19 @@ +[Unit] +Description= This Platform Monitor service is to initialize platform and monitor platform. +Before=platform-monitor.service +After=sysinit.target +Wants=fancontrol.service +Wants=qsfp-monitor.service +DefaultDependencies=no + +[Service] +ExecStartPre=/usr/sbin/i2c_utils.sh i2c_init +ExecStart=/usr/sbin/s9200_64x_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/utils/i2c_utils.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/utils/i2c_utils.sh new file mode 100644 index 000000000000..d0ca56f4d1b0 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/utils/i2c_utils.sh @@ -0,0 +1,1942 @@ +#!/bin/bash + +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +VERSION="1.0.0" +TRUE=200 +FALSE=404 + +EXEC_FUNC=${1} +COLOR_LED=${2} +QSFP_PORT=${2} +QSFP_INIT_ACTION=${2} +MB_EEPROM_ACTION=${2} +TARGET_10G_MUX=${2} +QSFP_ACTION=${3} +QSFP_VALUE=${4} +ONOFF_LED=${3} +FAN_TRAY=${4} + +############################################################ +# Distributor ID: Debian +# Description: Debian GNU/Linux 8.6 (jessie) +# Release: 8.6 +# Codename: jessie +# Linux debian 3.16.0-4-amd64 #1 +# SMP Debian 3.16.36-1+deb8u1 (2016-09-03) x86_64 GNU/Linux +############################################################ + +# Color Definition +COLOR_TITLE="\e[1;32m" ### Green ### +COLOR_WARNING="\e[1;33m" ### Yellow ### +COLOR_ERROR="\e[1;31m" ### Red ### +COLOR_END="\e[0m" ### END ### + +I2C_NUM=0 +NUM_I801_DEVICE=$((I2C_NUM++)) #0 + +#MUX PCA9548#0 CPLD (I2C_NUM 1~8) +NUM_MUX_9548_CPLD_CHAN=() +for (( i=0; i<8; ++i )) +do + NUM_MUX_9548_CPLD_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9548#1 UCD (I2C_NUM 9~16) +NUM_MUX_9548_UCD_CHAN=() +for (( i=0; i<8; ++i )) +do + NUM_MUX_9548_UCD_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9545 PSU (I2C_NUM 17~20) +NUM_MUX_9545_PSU_CHAN=() +for (( i=0; i<4; ++i )) +do + NUM_MUX_9545_PSU_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9548 QSFP ROOT (I2C_NUM 21~28) +NUM_MUX_9548_QSFP_ROOT_CHAN=() +for (( i=0; i<8; ++i )) +do + NUM_MUX_9548_QSFP_ROOT_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9548 QSFP Port 0~7 (I2C_NUM 29~36) +NUM_MUX_9548_QSFP_0_CHAN=() +for (( i=0; i<8; ++i )) +do + NUM_MUX_9548_QSFP_0_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9548 QSFP Port 8~15 (I2C_NUM 37~44) +NUM_MUX_9548_QSFP_1_CHAN=() +for (( i=0; i<8; ++i )) +do + NUM_MUX_9548_QSFP_1_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9548 QSFP Port 16~23 (I2C_NUM 45~52) +NUM_MUX_9548_QSFP_2_CHAN=() +for (( i=0; i<8; ++i )) +do + NUM_MUX_9548_QSFP_2_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9548 QSFP Port 24~31 (I2C_NUM 53~60) +NUM_MUX_9548_QSFP_3_CHAN=() +for (( i=0; i<8; ++i )) +do + NUM_MUX_9548_QSFP_3_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9548 QSFP Port 32~39 (I2C_NUM 61~68) +NUM_MUX_9548_QSFP_4_CHAN=() +for (( i=0; i<8; ++i )) +do + NUM_MUX_9548_QSFP_4_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9548 QSFP Port 40~47 (I2C_NUM 69~76) +NUM_MUX_9548_QSFP_5_CHAN=() +for (( i=0; i<8; ++i )) +do + NUM_MUX_9548_QSFP_5_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9548 QSFP Port 48~55 (I2C_NUM 77~84) +NUM_MUX_9548_QSFP_6_CHAN=() +for (( i=0; i<8; ++i )) +do + NUM_MUX_9548_QSFP_6_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9548 QSFP Port 56~63 (I2C_NUM 85~92) +NUM_MUX_9548_QSFP_7_CHAN=() +for (( i=0; i<8; ++i )) +do + NUM_MUX_9548_QSFP_7_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9545 SFP (I2C_NUM 93~96) +NUM_MUX_9545_SFP_CHAN=() +for (( i=0; i<4; ++i )) +do + NUM_MUX_9545_SFP_CHAN[i]=$((I2C_NUM++)) +done + +#MUX PCA9548 CPLD SW Upgrade (I2C_NUM 97~104) +NUM_MUX_9548_CPLD_SW_CHAN=() +for (( i=0; i<8; ++i )) +do + NUM_MUX_9548_CPLD_SW_CHAN[i]=$((I2C_NUM++)) +done + +#MUX Alias +I2C_BUS_PSU1_EEPROM=${NUM_MUX_9545_PSU_CHAN[1]} +I2C_BUS_PSU2_EEPROM=${NUM_MUX_9545_PSU_CHAN[0]} +I2C_BUS_LED_BOARD=${NUM_MUX_9548_UCD_CHAN[1]} +I2C_BUS_ID_GPIO=${NUM_MUX_9548_UCD_CHAN[2]} +I2C_BUS_10GMUX=${NUM_MUX_9548_UCD_CHAN[4]} +I2C_BUS_HWM=${NUM_MUX_9548_UCD_CHAN[7]} +I2C_BUS_FAN_BOARD=${NUM_I801_DEVICE} +I2C_BUS_MB_EEPROM=${NUM_I801_DEVICE} +I2C_BUS_TH_RESET=${NUM_I801_DEVICE} +I2C_BUS_BOARD_TYPE=${NUM_I801_DEVICE} +I2C_BUS_BMC_BOARD_TYPE=${NUM_I801_DEVICE} +I2C_BUS_LM75_MAC=${NUM_MUX_9548_CPLD_CHAN[5]} +I2C_BUS_LM75_FRONT=${NUM_MUX_9548_CPLD_CHAN[6]} +I2C_BUS_LM75_REAR=${NUM_MUX_9548_CPLD_CHAN[5]} +I2C_BUS_PSU_INT=${NUM_I801_DEVICE} +I2C_BUS_PSU_STATUS=${NUM_I801_DEVICE} +I2C_BUS_MUX_RST=${NUM_I801_DEVICE} +#CPU Board +I2C_BUS_CPU_EEPROM=${NUM_I801_DEVICE} +I2C_BUS_CPU_TMP75=${NUM_I801_DEVICE} +I2C_BUS_MUX_CPU_CPLD=${NUM_I801_DEVICE} + +PATH_SYS_I2C_DEVICES="/sys/bus/i2c/devices" +PATH_SYS_CPLD="/sys/devices/platform/ingrasys-s9200-cpld.0" +PATH_HWMON_ROOT_DEVICES="/sys/class/hwmon" +PATH_HWMON_W83795_DEVICE="${PATH_HWMON_ROOT_DEVICES}/hwmon5" +PATH_I801_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_I801_DEVICE}" +PATH_LM75_MAC="${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_LM75_MAC}" +PATH_LM75_FRONT="${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_LM75_FRONT}" +PATH_LM75_REAR="${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_LM75_REAR}" +PATH_CPU_TMP75="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_I801_DEVICE}" +PATH_BMC_TMP75="${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_HWM}" +PATH_PSU1_EERPOM=${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_PSU1_EEPROM} +PATH_PSU2_EEPROM=${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_PSU2_EEPROM} +PATH_10GMUX=${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_10GMUX} +#PATH for MUX PCA9548_QSFP_ROOT +PATH_MUX_9548_QSFP_ROOT="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9545_PSU_CHAN[2]}" +PATH_MUX_9548_QSFP_ROOT_CHAN=() +for (( i=0; i<8; ++i )) +do + PATH_MUX_9548_QSFP_ROOT_CHAN[i]="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_QSFP_ROOT_CHAN[i]}" +done +PATH_MUX_9545_SFP="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9545_PSU_CHAN[3]}" +PATH_MUX_9545_SFP_CHAN=() +for (( i=0; i<4; ++i )) +do + PATH_MUX_9545_SFP_CHAN[i]="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9545_SFP_CHAN[i]}" +done + +#i2cmap and gpiomap path +PATH_I2CMAP=/tmp/i2cmap + +#Mother Board +I2C_ADDR_MUX_9539_TH_RST=0x74 +I2C_ADDR_LED_BOARD=0x75 +I2C_ADDR_FAN_BOARD=0x20 +I2C_ADDR_ID_GPIO=0x27 +I2C_ADDR_MUX_9548_CPLD=0x70 +I2C_ADDR_MUX_9548_UCD=0x76 +I2C_ADDR_MUX_9545_PSU=0x72 +I2C_ADDR_MUX_9548_QSFP_ROOT=0x71 +I2C_ADDR_MUX_9548_QSFP=0x73 +I2C_ADDR_MUX_9545_SFP=0x73 +I2C_ADDR_CPLD=0x33 +I2C_ADDR_MB_EEPROM=0x55 +I2C_ADDR_QSFP_EEPROM=0x50 +I2C_ADDR_PSU_EEPROM=0x50 +I2C_ADDR_LM75_MAC=0x4E +I2C_ADDR_LM75_FRONT=0x4D +I2C_ADDR_LM75_REAR=0x4D +I2C_ADDR_10GMUX=0x67 +#Dummy BMC Board +I2C_ADDR_HWM=0x2F +I2C_ADDR_PSU_INT=0x24 #PSU Interrup on Dummy BMC Board +I2C_ADDR_BMC_BOARD_TYPE=0x24 #PSU Status on Dummy BMC Board +I2C_ADDR_PSU_STATUS=0x25 #PSU Status on Dummy BMC Board +I2C_ADDR_MUX_RST=0x26 #MUX RST on Dummy BMC Board +I2C_ADDR_TMP75_BB=0x4A +#CPU Board +I2C_ADDR_CPU_EEPROM=0x51 +I2C_ADDR_CPU_TMP75=0x4F +I2C_ADDR_MUX_CPU_CPLD=0x77 + +#sysfs +PATH_SYSFS_PSU1="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_PSU1_EEPROM}-$(printf "%04x" $I2C_ADDR_PSU_EEPROM)" +PATH_SYSFS_PSU2="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_PSU2_EEPROM}-$(printf "%04x" $I2C_ADDR_PSU_EEPROM)" + +#Active High/Low +ACTIVE_LOW=1 +ACTIVE_HIGH=0 +#GPIO Direction In/Out +DIR_IN=in +DIR_OUT=out + +#Power Supply Status +PSU_DC_ON=1 +PSU_DC_OFF=0 +PSU_EXIST=1 +PSU_NOT_EXIST=0 + +#W83795 Registers +REG_BANK_SEL=0x00 +REG_VOLT_CTRL1=0x02 +REG_VOLT_CTRL2=0x03 +REG_TEMP_CTRL1=0x04 +REG_TEMP_CTRL2=0x05 + +#PCA9535 Registers +REG_IN_0=0 +REG_IN_1=1 +REG_OUT_0=2 +REG_OUT_1=3 +REG_POLARITY_0=4 +REG_POLARITY_1=5 +REG_CFG_0=6 +REG_CFG_1=7 + +#LM75 Registers +LM75_REG_TEMP=0x00 +LM75_REG_CONF=0x01 +LM75_REG_HYST=0x02 +LM75_REG_TOS=0x03 + +#Bit Mask +BIT_MASK=(1 2 4 8 16 32 64 128) + +#MUX Type +PCA9545="pca9545" +PCA9548="pca9548" + +#PLATFORM Variables +PORT_NUM=64 + +#CPLD Variables +CPLD_QSFP_GROUP=(12 25 38 51 64) +CPLD_QSFP_STATUS_REG_BASE=0x20 +CPLD_QSFP_MODE_SELECT_REG_BASE=0x30 +CPLD_QSFP_ABS="abs" +CPLD_QSFP_INT="int" +CPLD_QSFP_RST="rst" +CPLD_QSFP_LP="lp" + + +# Help usage function +function _help { + echo "=========================================================" + echo "# Description: Help Function" + echo "=========================================================" + echo "----------------------------------------------------" + echo "EX : ${0} help" + echo " : ${0} i2c_init" + echo " : ${0} i2c_deinit" + echo " : ${0} i2c_temp_init" + echo " : ${0} i2c_fan_init" + echo " : ${0} i2c_volmon_init" + echo " : ${0} i2c_io_exp_init" + echo " : ${0} i2c_psu_eeprom_get" + echo " : ${0} i2c_mb_eeprom_get" + echo " : ${0} i2c_cpu_eeprom_get" + echo " : ${0} i2c_qsfp_eeprom_get [1-${PORT_NUM}]" + echo " : ${0} i2c_qsfp_eeprom_init new|delete" + echo " : ${0} i2c_mb_eeprom_init new|delete" + echo " : ${0} i2c_cpu_eeprom_init new|delete" + echo " : ${0} i2c_qsfp_status_get [1-${PORT_NUM}]" + echo " : ${0} i2c_qsfp_type_get [1-${PORT_NUM}]" + echo " : ${0} i2c_qsfp_signal_get [1-${PORT_NUM}] [abs|int|rst|lp]" + echo " : ${0} i2c_qsfp_signal_set [1-${PORT_NUM}] [rst|lp] [0|1] (low_active)" + echo " : ${0} i2c_board_type_get" + echo " : ${0} i2c_bmc_board_type_get" + echo " : ${0} i2c_psu_status" + echo " : ${0} i2c_led_psu_status_set" + echo " : ${0} i2c_led_fan_status_set" + echo " : ${0} i2c_led_fan_tray_status_set" + echo " : ${0} i2c_led_fan_tray_test" + echo " : ${0} i2c_cpld_version" + echo " : ${0} i2c_test_all" + echo " : ${0} i2c_led_test" + echo " : ${0} i2c_sys_led green|amber" + echo " : ${0} i2c_fan_led green|amber" + echo " : ${0} i2c_psu1_led green|amber" + echo " : ${0} i2c_psu2_led green|amber" + echo " : ${0} i2c_fan_tray_led green|amber on|off [1-4]" + echo " : ${0} i2c_10g_mux cpu|fp" + echo "----------------------------------------------------" +} + +#Pause function +function _pause { + read -p "$*" +} + +#Retry command function +function _retry { + local i + for i in {1..5}; + do + eval "${*}" && break || echo "retry"; sleep 1; + done +} + +#logical(front panel) to physical (falcon core) port mapping +function _port_logic2phy { + + local logic_port=$1 + local phy_port=0 + + if (( $logic_port >=1 && $logic_port <= 32 )) ; then + phy_port=$(( (logic_port-1)/2*4 + (logic_port-1)%2 + 1)) + elif (( $logic_port >=33 && $logic_port <= 64 )) ; then + phy_port=$(( (((logic_port-1)%32))/2*4 + (logic_port-1)%2 + 3)) + fi + + echo $phy_port +} + +#set i2cmap +function _set_i2cmap { + local i2c_n=$1 + local alias=$2 + + #create i2cmap dir if not exist + mkdir -p $PATH_I2CMAP + + #check i2c_n exists in sysfs + if [ ! -L ${PATH_SYS_I2C_DEVICES}/i2c-${i2c_n} ]; then + echo "${PATH_SYS_I2C_DEVICES}/i2c-${i2c_n} does not exist." + return + fi + + #create or update link + ln -sf ${PATH_SYS_I2C_DEVICES}/i2c-${i2c_n} ${PATH_I2CMAP}/${alias} +} + +#clear i2cmap +function _clear_i2cmap { + #delete i2cmap dir + rm -rf ${PATH_I2CMAP}/ +} + +#remove kernel module if exists +function _util_rmmod { + local mod=$1 + [ "$(lsmod | grep "^$mod ")" != "" ] && rmmod $mod +} + +function _create_i2c_mux_device { + local mux_type=$1 + local mux_addr=$2 + local mux_parent_chan=$3 + local mux_chan0=$4 + local mux_desc=$5 + + if [ ! -e "${PATH_SYS_I2C_DEVICES}/i2c-${mux_chan0}" ]; then + _retry "echo '$mux_type ${mux_addr}' > ${mux_parent_chan}/new_device" + _set_i2cmap ${mux_chan0} "${mux_desc}" + else + echo "${mux_desc} ${mux_addr} already init." + fi +} + +#I2C Init +function _i2c_init { + echo "=========================================================" + echo "# Description: I2C Init" + echo "=========================================================" + + _util_rmmod i2c_i801 + modprobe i2c_i801 #disable_features=0x10 + modprobe i2c_dev + modprobe i2c_mux_pca954x force_deselect_on_exit=1 + + #add MUX_9548_CPLD on I801 + _create_i2c_mux_device $PCA9548 $I2C_ADDR_MUX_9548_CPLD ${PATH_I801_DEVICE} ${NUM_MUX_9548_CPLD_CHAN[0]} "PCA9548_CPLD" + + #add MUX_9548_UCD on I801 + _create_i2c_mux_device $PCA9548 $I2C_ADDR_MUX_9548_UCD ${PATH_I801_DEVICE} ${NUM_MUX_9548_UCD_CHAN[0]} "PCA9548_UCD" + + #add MUX_9545_PSU on I801 + _create_i2c_mux_device $PCA9545 $I2C_ADDR_MUX_9545_PSU ${PATH_I801_DEVICE} ${NUM_MUX_9545_PSU_CHAN[0]} "PCA9545_PSU" + + #add MUX_9548_QSFP_ROOT on MUX_9545_PSU_CHAN[2] + _create_i2c_mux_device $PCA9548 $I2C_ADDR_MUX_9548_QSFP_ROOT ${PATH_MUX_9548_QSFP_ROOT} ${NUM_MUX_9548_QSFP_ROOT_CHAN[0]} "PCA9548_QSPF_ROOT" + + #add MUX_9548_QSFP_0 on MUX_9548_QSFP_ROOT_CHAN[0] + _create_i2c_mux_device $PCA9548 $I2C_ADDR_MUX_9548_QSFP ${PATH_MUX_9548_QSFP_ROOT_CHAN[0]} ${NUM_MUX_9548_QSFP_0_CHAN[0]} "PCA9548_QSPF_0" + + #add MUX_9548_QSFP_1 on MUX_9548_QSFP_ROOT_CHAN[1] + _create_i2c_mux_device $PCA9548 $I2C_ADDR_MUX_9548_QSFP ${PATH_MUX_9548_QSFP_ROOT_CHAN[1]} ${NUM_MUX_9548_QSFP_1_CHAN[0]} "PCA9548_QSPF_1" + + #add MUX_9548_QSFP_2 on MUX_9548_QSFP_ROOT_CHAN[2] + _create_i2c_mux_device $PCA9548 $I2C_ADDR_MUX_9548_QSFP ${PATH_MUX_9548_QSFP_ROOT_CHAN[2]} ${NUM_MUX_9548_QSFP_2_CHAN[0]} "PCA9548_QSPF_2" + + #add MUX_9548_QSFP_3 on MUX_9548_QSFP_ROOT_CHAN[3] + _create_i2c_mux_device $PCA9548 $I2C_ADDR_MUX_9548_QSFP ${PATH_MUX_9548_QSFP_ROOT_CHAN[3]} ${NUM_MUX_9548_QSFP_3_CHAN[0]} "PCA9548_QSPF_3" + + #add MUX_9548_QSFP_4 on MUX_9548_QSFP_ROOT_CHAN[4] + _create_i2c_mux_device $PCA9548 $I2C_ADDR_MUX_9548_QSFP ${PATH_MUX_9548_QSFP_ROOT_CHAN[4]} ${NUM_MUX_9548_QSFP_4_CHAN[0]} "PCA9548_QSPF_4" + + #add MUX_9548_QSFP_5 on MUX_9548_QSFP_ROOT_CHAN[5] + _create_i2c_mux_device $PCA9548 $I2C_ADDR_MUX_9548_QSFP ${PATH_MUX_9548_QSFP_ROOT_CHAN[5]} ${NUM_MUX_9548_QSFP_5_CHAN[0]} "PCA9548_QSPF_5" + + #add MUX_9548_QSFP_6 on MUX_9548_QSFP_ROOT_CHAN[6] + _create_i2c_mux_device $PCA9548 $I2C_ADDR_MUX_9548_QSFP ${PATH_MUX_9548_QSFP_ROOT_CHAN[6]} ${NUM_MUX_9548_QSFP_6_CHAN[0]} "PCA9548_QSPF_6" + + #add MUX_9548_QSFP_7 on MUX_9548_QSFP_ROOT_CHAN[7] + _create_i2c_mux_device $PCA9548 $I2C_ADDR_MUX_9548_QSFP ${PATH_MUX_9548_QSFP_ROOT_CHAN[7]} ${NUM_MUX_9548_QSFP_7_CHAN[0]} "PCA9548_QSPF_7" + + #add MUX_9545_SFP on QSFP_ROOT + _create_i2c_mux_device $PCA9545 $I2C_ADDR_MUX_9545_SFP ${PATH_MUX_9545_SFP} ${NUM_MUX_9545_SFP_CHAN[0]} "PCA9545_SPF" + + #Init CPLD LED_CLR Register (QSFP LED) + i2cset -y -m ${BIT_MASK[2]} ${NUM_I801_DEVICE} ${I2C_ADDR_MUX_9539_TH_RST} ${REG_OUT_0} 0xFF + + _util_rmmod coretemp + _util_rmmod jc42 + _util_rmmod w83795 + _util_rmmod lm75 + + _i2c_temp_init + _i2c_volmon_init + modprobe coretemp + modprobe lm75 + modprobe jc42 + modprobe eeprom_mb + _i2c_mb_eeprom_init "new" + _i2c_cpu_eeprom_init "new" + modprobe eeprom + modprobe gpio-pca953x + _i2c_sensors_init + _i2c_fan_init + _i2c_io_exp_init + _i2c_psu_init + _i2c_qsfp_eeprom_init "new" + _i2c_led_psu_status_set + _i2c_led_fan_status_set + COLOR_LED="green" + echo "${COLOR_LED}" + _i2c_sys_led + modprobe cpld + _config_rmem +} + +#I2C Deinit +function _i2c_deinit { + for mod in cpld coretemp jc42 w83795 lm75 eeprom eeprom_mb gpio-pca953x i2c_mux_pca954x i2c_i801 ingrasys_s9200_64x_psu; + do + _util_rmmod $mod + done + _clear_i2cmap +} + +#Temperature sensor Init +function _i2c_temp_init { + local lm="lm75" + local tmp="tmp75" + local i2c_bus=${I2C_BUS_HWM} + local i2c_addr=${I2C_ADDR_HWM} + + echo "=========================================================" + echo "# Description: TEMP INIT" + echo "=========================================================" + + # select bank0 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_BANK_SEL} 0x80 + echo "enable VDSEN14, VDSEN15, VDSEN16, and TR4 temperature monitoring" + i2cset -y -r ${i2c_bus=} ${i2c_addr} ${REG_TEMP_CTRL2} 0x6A + + echo "export CPU sensor TMP75 to sysfs" + echo "${tmp} ${I2C_ADDR_CPU_TMP75}" > ${PATH_CPU_TMP75}/new_device + + echo "Add Dummy Board LM75 to sysfs" + echo "${lm} ${I2C_ADDR_LM75_MAC}" > ${PATH_LM75_MAC}/new_device + echo "${lm} ${I2C_ADDR_LM75_FRONT}" > ${PATH_LM75_FRONT}/new_device + echo "${lm} ${I2C_ADDR_LM75_REAR}" > ${PATH_LM75_REAR}/new_device + + echo "CLKIN clock frequency set as 48Mhz" + i2cset -y -r ${i2c_bus} ${i2c_addr} 0x01 0x1C + + # select bank 2 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_BANK_SEL} 0x82 + echo "set PWM mode in FOMC" + i2cset -y -r ${i2c_bus} ${i2c_addr} 0x0F 0x00 +} + +#FAN Init +function _i2c_fan_init { + local init_fan_speed=120 + + echo "=========================================================" + echo "# Description: FAN INIT" + echo "=========================================================" + + if [ -e "${PATH_HWMON_W83795_DEVICE}" ]; then + echo "Init Fan Speed to ${init_fan_speed} (MAX is 255)" + echo $init_fan_speed > ${PATH_HWMON_W83795_DEVICE}/device/pwm1 + echo $init_fan_speed > ${PATH_HWMON_W83795_DEVICE}/device/pwm2 + else + echo "FAIL, W83795 not found in path ${PATH_HWMON_W83795_DEVICE}" + fi +} + +#VOLMON Init +function _i2c_volmon_init { + echo -n "VOLMON INIT" + local i2c_bus=${I2C_BUS_HWM} + local i2c_addr=${I2C_ADDR_HWM} + + #select bank0 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_BANK_SEL} 0x80 + #enable voltage monitoring VSEN 1~8 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_VOLT_CTRL1} 0xFF + #enable voltage monitoring 3VDD and 3VBAT + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_VOLT_CTRL2} 0x50 + #eable voltage monitoring VSEN12 and VSEN13 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_TEMP_CTRL1} 0x0A + + echo "Done" +} + +#IO Expander Init +function _i2c_io_exp_init { + local i2c_bus=0 + local i2c_addr=0 + + echo "=========================================================" + echo "# Description: I2C IO Expander Init" + echo "=========================================================" + + echo "Init PCA9539 TH RESET IO Expander" + #PCA9539 TH RESET + #0.0 TH_RST_L OUT 1 + #0.1 TH_PCIE_RST_L OUT 1 + #0.2 LED_CLR OUT 1 + #0.3 HOST_TO_BMC_I2C_GPIO OUT 0 + #0.4 USB_MUX_SEL OUT 0 + #0.5 UART_MUX_SEL OUT 0 + #0.6 FIN0 OUT 0 + #0.7 FIN7 OUT 0 + #1.0 TH_INT_L IN + #1.1 CPLD4_TO_CPU_INT_L IN + #1.2 CPLD3_TO_CPU_INT_L IN + #1.3 CPLD2_TO_CPU_INT_L IN + #1.4 CPLD1_TO_CPU_INT_L IN + #1.5 REF_SEL OUT 0 + #1.6 I210_PE_RST_L OUT 1 + #1.7 I210_RST_L OUT 1 + i2c_bus=${I2C_BUS_TH_RESET} + i2c_addr=${I2C_ADDR_MUX_9539_TH_RST} + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_OUT_0} $((2#00000111)) + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_OUT_1} $((2#11000000)) + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_0} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_1} $((2#00011111)) + + echo "Init PCA9539 LED BOARD IO Expander" + #PCA9539 LED BOARD + #0.0 SYS_LED_G OUT 1 + #0.1 FAN_LED_EN OUT 1 + #0.2 FAN_LED_Y OUT 0 + #0.3 PSU1_LED_Y OUT 0 + #0.4 PSU0_LED_Y OUT 0 + #0.5~0.7 X + #1.0 PSU1_PWROK OUT 1 + #1.1 PSU0_PWROK OUT 1 + i2c_bus=${I2C_BUS_LED_BOARD} + i2c_addr=${I2C_ADDR_LED_BOARD} + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_OUT_0} $((2#00000011)) + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_OUT_1} $((2#00000011)) + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_0} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_1} 0x00 + + echo "Init PCA9535 ID_GPIO IO Expander" + #PCA9535 ID_GPIO + #0.0 ~ 0.7 X + #1.0 Build_REV_0 IN + #1.1 Build_REV_1 IN + #1.2 HW_REV_0 IN + #1.3 HW_REV_1 IN + #1.4 Board_ID_0 IN + #1.5 Board_ID_1 IN + #1.6 Board_ID_2 IN + #1.7 Board_ID_3 IN + i2c_bus=${I2C_BUS_ID_GPIO} + i2c_addr=${I2C_ADDR_ID_GPIO} + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_0} 0xFF + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_1} 0xFF + + #I2C_ADDR_FAN_BOARD + echo "Init PCA9535 FAN_BOARD IO Expander" + #PCA9535 FAN_BOARD + #0.0 EN_FAN1_LED_G_H OUT 1 + #0.1 EN_FAN1_LED_Y_H OUT 0 + #0.2 FAN1_ABS# IN 0 + #0.3 FAN1_DIR# IN 0 + #0.4 EN_FAN2_LED_G_H OUT 1 + #0.5 EN_FAN2_LED_Y_H OUT 0 + #0.6 FAN2_ABS# IN 0 + #0.7 FAN2_DIR# IN 0 + #1.0 EN_FAN3_LED_G_H OUT 1 + #1.1 EN_FAN3_LED_Y_H OUT 0 + #1.2 FAN3_ABS# IN 0 + #1.3 FAN3_DIR# IN 0 + #1.4 EN_FAN4_LED_G_H OUT 1 + #1.5 EN_FAN4_LED_Y_H OUT 0 + #1.6 FAN4_ABS# IN 0 + #1.7 FAN4_DIR# IN 0 + i2c_bus=${I2C_BUS_FAN_BOARD} + i2c_addr=${I2C_ADDR_FAN_BOARD} + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_OUT_0} $((2#00010001)) + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_OUT_1} $((2#00010001)) + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_0} $((2#11001100)) + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_1} $((2#11001100)) + + #PCA9555 on BMC Dummy Board + echo "Init PCA9555 Interrupt IO Expander" + #PCA9555 Interrupt Expander + #0.0 HWM_INT_L IN + #0.1 UCD9090_INT_L IN + #0.2 ETHX_INT_L IN + #0.3 HOST_TO_BMC_INT_L IN + #0.4 THERMAL_ALT1# IN + #0.5 FAN_STATUS_INT_L IN + #0.6 PSU0_INT_L IN + #0.7 PSU1_INT_L IN + #1.0 Build_REV_0 IN + #1.1 Build_REV_1 IN + #1.2 HW_REV_0 IN + #1.3 HW_REV_1 IN + #1.4 Board_ID_0 IN + #1.5 Board_ID_1 IN + #1.6 Board_ID_2 IN + #1.7 Board_ID_3 IN + i2c_bus=${I2C_BUS_PSU_INT} + i2c_addr=${I2C_ADDR_PSU_INT} + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_0} 0xFF + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_1} 0xFF + + #PCA9555 on BMC Dummy Board + echo "Init PCA9555 PSU0_PWROK IO Expander" + #PCA9555 PSU0_PWROK Expander + #0.0 PSU0_PWROK IN + #0.1 PSU0_PRSNT_L IN + #0.2 PSU0_PWRON_L OUT 0 + #0.3 PSU1_PWROK IN + #0.4 PSU1_PRSNT_L IN + #0.5 PSU1_PWRON_L OUT 0 + #0.6 TMP75_INT_L IN + #0.7 GPIOF7 IN + #1.0 ALL_PWR_GOOD IN + #1.1 CPU_PRSNT_L IN + #1.2 FP_BTN_UARTSEL_EN_L OUT 0 + #1.3 HOST_TO_BMC_I2C_GPIO OUT 0 + #1.4 HOST_CPU_PWRBTN_L OUT 1 + #1.5 N/A + #1.6 N/A + #1.7 N/A + i2c_bus=${I2C_BUS_PSU_STATUS} + i2c_addr=${I2C_ADDR_PSU_STATUS} + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_OUT_0} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_OUT_1} $((2#00010000)) + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_0} $((2#11011011)) + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_1} $((2#11100011)) + + #PCA9555 on BMC Dummy Board + echo "Init PCA9555 MUX RST IO Expander" + #PCA9555 MUX RST Expander + #0.0 FRU_I2C_MUX_RST_L OUT 1 + #0.1 MAIN_I2C_MUX_RST_L OUT 1 + #0.2 SYSTEM_LED_RST_L OUT 1 + #0.3 BMC_TO_UCD_RST_L OUT 1 + #0.4 BMC_TO_HOST_RST_L OUT 1 + #0.5 ETHX_RST_L OUT 1 + #0.6 GPIOF4 IN + #0.7 N/A + #1.0 CPU_to_FRU_I2C_EN OUT 1 + #1.1 CPU_to_MAIN_I2C_EN OUT 1 + #1.2 P3V3_FAN_EN OUT 1 + #1.3 P3V3_I2C_EN OUT 1 + #1.4 UCD9090_CNTRL OUT 1 + #1.5 UART_MUX_SEL OUT 0 + #1.6 USB_MUX_SEL OUT 0 + #1.7 ETHX_CPU_EEPROM_SEL OUT 0 + i2c_bus=${I2C_BUS_MUX_RST} + i2c_addr=${I2C_ADDR_MUX_RST} + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_OUT_0} $((2#00111111)) + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_OUT_1} $((2#00011111)) + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_0} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_POLARITY_1} 0x00 + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_0} $((2#01000000)) + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_1} 0x00 + + #PCA9539 on CPU Board + echo "Init PCA9539 CPLD IO Expander on CPU" + i2c_bus=${I2C_BUS_MUX_CPU_CPLD} + i2c_addr=${I2C_ADDR_MUX_CPU_CPLD} + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_0} 0xFF + i2cset -y -r ${i2c_bus} ${i2c_addr} ${REG_CFG_1} 0xFF +} + +function _i2c_sensors_init { + echo "SENSORS init" + local dev_path + + # add w83795 to sysfs + dev_path="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_HWM}-$(printf "%04x" ${I2C_ADDR_HWM})" + if ! [ -L ${dev_path} ]; then + echo "w83795adg ${I2C_ADDR_HWM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_HWM}/new_device + while [ ! -e "${PATH_HWMON_W83795_DEVICE}" ]; do + sleep 1 + done + else + echo "${dev_path} already exist (w83795)" + fi + + # BMC board thermal + dev_path="${PATH_SYS_I2C_DEVICES}/${NUM_I801_DEVICE}-$(printf "%04x" ${I2C_ADDR_TMP75_BB})" + if ! [ -L ${dev_path} ]; then + echo "tmp75 ${I2C_ADDR_TMP75_BB}" > ${PATH_BMC_TMP75}/new_device + else + echo "${dev_path} already exist" + fi + + echo "Done" +} + +#Set FAN Tray LED +function _i2c_led_fan_tray_status_set { + echo "=========================================================" + echo "# Description: FAN Tray Status Setup" + echo "=========================================================" + + #check W83795 exists + if [ ! -e "${PATH_HWMON_W83795_DEVICE}" ]; then + echo "FAIL, W83795 not found in path ${PATH_HWMON_W83795_DEVICE}" + return + fi + #FAN Status get + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + + FAN_TRAY=1 + if [ "${FAN1_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + _i2c_fan_tray_led + + COLOR_LED="amber" + ONOFF_LED="off" + _i2c_fan_tray_led + + echo "set [FAN TRAY 1] [Green]=on [Amber]=off" + else + COLOR_LED="green" + ONOFF_LED="off" + _i2c_fan_tray_led + + COLOR_LED="amber" + ONOFF_LED="on" + _i2c_fan_tray_led + + echo "set [FAN TRAY 1] [Green]=off [Amber]=on" + fi + + FAN_TRAY=2 + if [ "${FAN3_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + _i2c_fan_tray_led + + COLOR_LED="amber" + ONOFF_LED="off" + _i2c_fan_tray_led + + echo "set [FAN TRAY 2] [Green]=on [Amber]=off" + else + COLOR_LED="green" + ONOFF_LED="off" + _i2c_fan_tray_led + + COLOR_LED="amber" + ONOFF_LED="on" + _i2c_fan_tray_led + + echo "set [FAN TRAY 2] [Green]=off [Amber]=on" + fi + + FAN_TRAY=3 + if [ "${FAN5_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + _i2c_fan_tray_led + + COLOR_LED="amber" + ONOFF_LED="off" + _i2c_fan_tray_led + + echo "set [FAN TRAY 3] [Green]=on [Amber]=off" + else + COLOR_LED="green" + ONOFF_LED="off" + _i2c_fan_tray_led + + COLOR_LED="amber" + ONOFF_LED="on" + _i2c_fan_tray_led + + echo "set [FAN TRAY 3] [Green]=off [Amber]=on" + fi + + FAN_TRAY=4 + if [ "${FAN7_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + _i2c_fan_tray_led + + COLOR_LED="amber" + ONOFF_LED="off" + _i2c_fan_tray_led + + echo "set [FAN TRAY 4] [Green]=on [Amber]=off" + else + COLOR_LED="green" + ONOFF_LED="off" + _i2c_fan_tray_led + + COLOR_LED="amber" + ONOFF_LED="on" + _i2c_fan_tray_led + + echo "set [FAN TRAY 4] [Green]=off [Amber]=on" + fi +} + +#Test FAN Tray LED +function _i2c_led_fan_tray_test { + echo "=========================================================" + echo "# Description: FAN Tray LED Test" + echo "=========================================================" + + for i in {1..4} + do + FAN_TRAY=$i + COLOR_LED="green" + ONOFF_LED="on" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + _i2c_fan_tray_led + _pause "Check [Fan Tray ${i} LED] = Green and Press [Enter] key to continue..." + + COLOR_LED="green" + ONOFF_LED="off" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + _i2c_fan_tray_led + _pause "Check [Fan Tray ${i} LED] = Amber and Press [Enter] key to continue..." + done +} +#Set FAN LED +function _i2c_led_fan_status_set { + echo "=========================================================" + echo "# Description: FAN Status Setup" + echo "=========================================================" + + #check W83795 exists in hwmon2 + if [ ! -e "${PATH_HWMON_W83795_DEVICE}" ]; then + echo "FAIL, W83795 not found in path ${PATH_HWMON_W83795_DEVICE}" + return + fi + + #PSU Status set + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + + if [ "${FAN1_ALARM}" == "0" ] \ + && [ "${FAN3_ALARM}" == "0" ] \ + && [ "${FAN5_ALARM}" == "0" ] \ + && [ "${FAN7_ALARM}" == "0" ] ; then + COLOR_LED="green" + _i2c_fan_led + else + COLOR_LED="amber" + _i2c_fan_led + fi + echo "set [FAN LED] = ${COLOR_LED}" +} + +#Set PSU LED on LED Board +function _i2c_led_psu_status_set { + + echo "=========================================================" + echo "# Description: PSU LED Status Setup" + echo "=========================================================" + + #Get PSU Status + _i2c_psu_status + + #PSU1 Status + echo "------------------------------" + if [ "${psu1Exist}" == ${PSU_EXIST} ]; then + if [ "${psu1PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + _i2c_psu1_led + else + COLOR_LED="amber" + _i2c_psu1_led + fi + else + COLOR_LED="amber" + _i2c_psu1_led + fi + echo "set [PSU1 LED] = ${COLOR_LED}" + + #PSU2 Status + echo "------------------------------" + if [ "${psu2Exist}" == ${PSU_EXIST} ]; then + if [ "${psu2PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + _i2c_psu2_led + else + COLOR_LED="amber" + #ONOFF_LED="on" + _i2c_psu2_led + fi + else + COLOR_LED="amber" + _i2c_psu2_led + fi + echo "set [PSU2 LED] = ${COLOR_LED}" +} + +#LED Test +function _i2c_led_test { + echo "=========================================================" + echo "# Description: I2C LED TEST" + echo "=========================================================" + #sys led (green) + _i2c_reset_led + COLOR_LED="green" + _i2c_sys_led + _pause 'Check [SYS LED] = Green and Press [Enter] key to continue...' + #sys led (amber) + _i2c_reset_led + COLOR_LED="amber" + _i2c_sys_led + _pause 'Check [SYS LED] = Amber and Press [Enter] key to continue...' + + #FAN led (green) + _i2c_reset_led + COLOR_LED="green" + _i2c_fan_led + _pause 'Check [FAN LED] Green and Press [Enter] key to continue...' + #FAN led (amber) + _i2c_reset_led + COLOR_LED="amber" + _i2c_fan_led + _pause 'Check [FAN LED] = Amber and Press [Enter] key to continue...' + + #PSU2 led (green) + _i2c_reset_led + COLOR_LED="green" + _i2c_psu2_led + _pause 'Check [PSU2 LED] = Green and Press [Enter] key to continue...' + #PSU2 led (amber) + _i2c_reset_led + COLOR_LED="amber" + _i2c_psu2_led + _pause 'Check [PSU2 LED] = Amber and Press [Enter] key to continue...' + + #PSU1 led (green) + _i2c_reset_led + COLOR_LED="green" + _i2c_psu1_led + _pause 'Check [PSU1 LED] = Green and Press [Enter] key to continue...' + #PSU2 led (amber) + _i2c_reset_led + COLOR_LED="amber" + _i2c_psu1_led + _pause 'Check [PSU1 LED] = Amber and Press [Enter] key to continue...' + + #Turn OFF All LED + _i2c_reset_led + _pause 'Check turn off all LEDs and Press [Enter] key to continue...' +} + +#Set QSFP Port variable +function _qsfp_port_i2c_var_set { + #global variables + cpld_group=0 + cpld_i2c_bus=0 + cpld_i2c_addr=$I2C_ADDR_CPLD + cpld_qsfp_status_reg_offset=0 + cpld_qsfp_mode_reg_offset=0 + + #local variables + local i=0 + local port=$1 + local port_group=$(( (port - 1)/8 )) + + case ${port_group} in + 0) + #the i2c expander base bus num to read qsfp eeprom + eeprombusbase=${NUM_MUX_9548_QSFP_0_CHAN[0]} + ;; + 1) + eeprombusbase=${NUM_MUX_9548_QSFP_1_CHAN[0]} + ;; + 2) + eeprombusbase=${NUM_MUX_9548_QSFP_2_CHAN[0]} + ;; + 3) + eeprombusbase=${NUM_MUX_9548_QSFP_3_CHAN[0]} + ;; + 4) + eeprombusbase=${NUM_MUX_9548_QSFP_4_CHAN[0]} + ;; + 5) + eeprombusbase=${NUM_MUX_9548_QSFP_5_CHAN[0]} + ;; + 6) + eeprombusbase=${NUM_MUX_9548_QSFP_6_CHAN[0]} + ;; + 7) + eeprombusbase=${NUM_MUX_9548_QSFP_7_CHAN[0]} + ;; + *) + echo "Please input 1~${PORT_NUM}" + ;; + esac + + #set cpld_group and cpld_i2c_bus according to port + for ((i=0; i<5; ++i)) + do + if (("${port}" <= "${CPLD_QSFP_GROUP[i]}")); + then + local port_offset=0 + cpld_group=$i + cpld_i2c_bus=${NUM_MUX_9548_CPLD_CHAN[i]} + + if ((i>0)) + then + port_offset=$(( port - CPLD_QSFP_GROUP[i-1] )) + else + port_offset=$port + fi + cpld_qsfp_status_reg_offset=$((CPLD_QSFP_STATUS_REG_BASE + port_offset)) + cpld_qsfp_mode_reg_offset=$((CPLD_QSFP_MODE_SELECT_REG_BASE + port_offset)) + + break + fi + done +} + +function _i2c_get { + local i2c_bus=$1 + local i2c_addr=$2 + local reg=$3 + echo `i2cget -y ${i2c_bus} ${i2c_addr} ${reg}` +} + +function _i2c_set { + local i2c_bus=$1 + local i2c_addr=$2 + local reg=$3 + local mask=$4 + local value=$5 + + echo `i2cset -m $mask -y -r ${i2c_bus} ${i2c_addr} ${reg} ${value}` +} + +#Set QSFP Port variable +function _qsfp_eeprom_var_set { + #local variables + local port=$1 + + #global variables + eeprombusidx=$(( (port - 1)%8 )) + + case $eeprombusidx in + 0) + eeprombus=$(( $eeprombusbase + 0 )) + ;; + 1) + eeprombus=$(( $eeprombusbase + 1 )) + ;; + 2) + eeprombus=$(( $eeprombusbase + 2 )) + ;; + 3) + eeprombus=$(( $eeprombusbase + 3 )) + ;; + 4) + eeprombus=$(( $eeprombusbase + 4 )) + ;; + 5) + eeprombus=$(( $eeprombusbase + 5 )) + ;; + 6) + eeprombus=$(( $eeprombusbase + 6 )) + ;; + 7) + eeprombus=$(( $eeprombusbase + 7 )) + ;; + esac + eepromAddr=${I2C_ADDR_QSFP_EEPROM} +} + +#Get QSFP EEPROM Information +function _i2c_qsfp_eeprom_get { + local value=0 + local status=0 + + echo "=========================================================" + echo "# Description: QSFP EEPROM GET" + echo "=========================================================" + + #status: 0 -> Down, 1 -> Up + _i2c_qsfp_signal_get ${QSFP_PORT} ${CPLD_QSFP_ABS} + + value=$qsfp_signal + status=$(( value?0:1 )) + + if [ $status = 0 ]; then + echo "[Fail] QSFP ${QSFP_PORT} is not present" + exit + fi + + echo "------------------------------" + cat ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/eeprom | hexdump -C +} + +#Init QSFP EEPROM +function _i2c_qsfp_eeprom_init { + echo "=========================================================" + echo "# Description: QSFP EEPROM INIT" + echo "=========================================================" + + #Action check + action=$1 + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init EEPROM + local i + for (( i=1; i<=${PORT_NUM}; i++ )) + do + _qsfp_port_i2c_var_set ${i} + + _qsfp_eeprom_var_set ${i} + + if [ "${action}" == "new" ] && \ + ! [ -L ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr) ]; then + echo "sff8436 $eepromAddr" > ${PATH_SYS_I2C_DEVICES}/i2c-$eeprombus/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr) ]; then + echo "$eepromAddr" > ${PATH_SYS_I2C_DEVICES}/i2c-$eeprombus/delete_device + fi + done +} + +#get qsfp singals (ABS, INT, RST, LP) from CPLD +function _i2c_qsfp_signal_get { + local port=$1 #port start from 1 + local signal=$2 + local attr_file="" + local phy_port=0 + local active="low_active" + + #global + qsfp_signal=0 + qsfp_signals=0 + + #verify port + if [ -z "${port}" ]; then + echo "No port, skip" + return + fi + + #verify port + if (( $port < 1 || $port > $PORT_NUM )) ; then + echo "invalid port, skip" + fi + + #get physical port + phy_port=$(_port_logic2phy $port) + + _qsfp_port_i2c_var_set ${phy_port} + _qsfp_eeprom_var_set ${phy_port} + + #verify signal + if [ -z "${signal}" ]; then + echo "No signal, skip" + return + elif [ "${signal}" == "${CPLD_QSFP_ABS}" ]; then + attr_file="qsfp_modprs" + elif [ "${signal}" == "${CPLD_QSFP_INT}" ]; then + attr_file="qsfp_int" + elif [ "${signal}" == "${CPLD_QSFP_RST}" ]; then + attr_file="qsfp_reset" + elif [ "${signal}" == "${CPLD_QSFP_LP}" ]; then + attr_file="qsfp_lpmode" + active="high_active" + else + echo "invalid signal ${signal}, skip" + return + fi + + mask=${BIT_MASK[bit_shift]} + + #signals of all ports + qsfp_signals=`cat ${PATH_SYS_CPLD}/${attr_file}` + #signal of single port + qsfp_signal=$(( (qsfp_signals >> (phy_port-1)) & 0x1)) + #print signals + printf "[port]=%d, [signal]=%s, [value]=%d (${active}), [qsfp_signals]=%016x\n" $port $signal $qsfp_signal $qsfp_signals +} + +#set qsfp singals (RST, LP) via CPLD +function _i2c_qsfp_signal_set { + local port=$1 #port start from 1 + local signal=$2 + local value=$3 + local values_ori=0 + local value_ori=0 + local values=0 + local attr_file="" + + #verify port + if [ -z "${port}" ]; then + echo "No port, skip" + return + fi + + #verify port + if (( $port < 1 || $port > $PORT_NUM )) ; then + echo "invalid port, skip" + fi + + #verify signal + if [ -z "${signal}" ]; then + echo "No signal, skip" + return + elif [ "${signal}" == "${CPLD_QSFP_RST}" ]; then + attr_file="qsfp_reset" + elif [ "${signal}" == "${CPLD_QSFP_LP}" ]; then + attr_file="qsfp_lpmode" + else + echo "invalid signal ${signal}, skip" + return + fi + + #get current signal values + _i2c_qsfp_signal_get $port $signal + value_ori=$qsfp_signal + values_ori=$qsfp_signals + + #if signal value is not changed, return + if [ "${value}" == "${value_ori}" ]; then + echo "value not changed, skip set operation" + return + fi + + #toggle the bit for update + values=$(( values_ori ^ (1 << (port-1)))) + + #set value to register + echo "$(printf "%016x" $values)" > ${PATH_SYS_CPLD}/${attr_file} + + #get updated signal values + _i2c_qsfp_signal_get $port $signal + values=$qsfp_signals + + #result + printf "Before: %016x\n" $values_ori + printf "After: %016x\n" $values +} + +#Init EEPROM +function _i2c_eeprom_init { + local i2c_bus=$1 + local i2c_addr=$2 + local action=$3 + local desc=$4 + local sys_i2c_dev=$PATH_SYS_I2C_DEVICES + + echo -n "${desc} EEPROM INIT" + + #Action check + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init EEPROM + if [ "${action}" == "new" ] && \ + ! [ -L ${sys_i2c_dev}/${i2c_bus}-$(printf "%04x" $i2c_addr) ]; then + echo "mb_eeprom ${i2c_addr}" > ${sys_i2c_dev}/i2c-${i2c_bus}/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${sys_i2c_dev}/${i2c_bus}-$(printf "%04x" $i2c_addr) ]; then + echo "${i2c_addr}" > ${sys_i2c_dev}/i2c-${i2c_bus}/delete_device + fi + echo "DONE" +} + +#Init Main Board EEPROM +function _i2c_mb_eeprom_init { + local action=$1 + local i2c_bus=$I2C_BUS_MB_EEPROM + local i2c_addr=$I2C_ADDR_MB_EEPROM + local desc="Main Board" + + _i2c_eeprom_init $i2c_bus $i2c_addr $action $desc +} + +#Init CPU EEPROM +function _i2c_cpu_eeprom_init { + local action=$1 + local i2c_bus=$I2C_BUS_CPU_EEPROM + local i2c_addr=$I2C_ADDR_CPU_EEPROM + local desc="CPU Board" + + _i2c_eeprom_init $i2c_bus $i2c_addr $action $desc +} + +#get QSFP Status +function _i2c_qsfp_status_get { + local value=0 + local status=0 + + #status: 0 -> Down, 1 -> Up + _i2c_qsfp_signal_get ${QSFP_PORT} ${CPLD_QSFP_ABS} + value=$qsfp_signal + status=$(( value?0:1 )) + echo "status=$status" +} + +#get QSFP Type +function _i2c_qsfp_type_get { + local phy_port=0 + local value=0 + local status=0 + + echo "=========================================================" + echo "# Description: QSFP TYPE Get" + echo "=========================================================" + + phy_port=$(_port_logic2phy ${QSFP_PORT}) + _qsfp_port_i2c_var_set ${phy_port} + _qsfp_eeprom_var_set ${phy_port} + + #get abs signal + _i2c_qsfp_signal_get ${QSFP_PORT} ${CPLD_QSFP_ABS} + value=$qsfp_signal + status=$(( value?0:1 )) + + #check qsfp is present + if [ $status = 0 ]; then + echo "[Fail] QSFP ${QSFP_PORT} is not present" + exit + fi + + #Get QSFP EEPROM info + if [ ! -e "${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf '%04x' $eepromAddr)/eeprom" ]; then + echo "FAIL, qsfp eeprom file does not exist. " + echo "qsfp eeprom file path: ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf '%04x' $eepromAddr)/eeprom" + return + fi + + qsfp_info=$(base64 ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/eeprom) + + identifier=$(echo $qsfp_info | base64 -d -i | hexdump -s 128 -n 1 -e '"%x"') + connector=$(echo $qsfp_info | base64 -d -i | hexdump -s 130 -n 1 -e '"%x"') + transceiver=$(echo $qsfp_info | base64 -d -i | hexdump -s 131 -n 1 -e '"%x"') + + echo "------------------------------" + echo "identifier = $identifier" + echo "connector = $connector" + echo "transceiver = $transceiver" +} + +#Init PSU Kernel Module +function _i2c_psu_init { + echo "=========================================================" + echo "# Description: I2C PSU Init" + echo "=========================================================" + modprobe ingrasys_s9200_64x_psu + + echo "psu1 ${I2C_ADDR_PSU_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_PSU1_EEPROM}/new_device + echo "psu2 ${I2C_ADDR_PSU_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_PSU2_EEPROM}/new_device +} + +#Get PSU EEPROM Information +function _i2c_psu_eeprom_get { + local eeprom_psu1="" + local eeprom_psu2="" + + echo "=========================================================" + echo "# Description: I2C PSU EEPROM Get..." + echo "=========================================================" + + echo "PUS1 EEPROM" + eeprom_psu1="${PATH_SYSFS_PSU1}/psu_eeprom" + cat ${eeprom_psu1} | hexdump -C + + echo "------------------------------" + echo "PUS2 EEPROM" + eeprom_psu2="${PATH_SYSFS_PSU2}/psu_eeprom" + cat ${eeprom_psu2} | hexdump -C +} + +#Get MotherBoard EEPROM Information +function _i2c_mb_eeprom_get { + local path_i2c_dev=${PATH_SYS_I2C_DEVICES} + local i2c_bus=${I2C_BUS_MB_EEPROM} + local i2c_addr=${I2C_ADDR_MB_EEPROM} + echo "=========================================================" + echo "# Description: I2C MB EEPROM Get" + echo "=========================================================" + + ## MB EEPROM + cat ${path_i2c_dev}/${i2c_bus}-$(printf "%04x" ${i2c_addr})/eeprom | hexdump -v -C +} + +#Get CPU EEPROM Information +function _i2c_cpu_eeprom_get { + local path_i2c_dev=${PATH_SYS_I2C_DEVICES} + local i2c_bus=${I2C_BUS_CPU_EEPROM} + local i2c_addr=${I2C_ADDR_CPU_EEPROM} + echo "=========================================================" + echo "# Description: I2C CPU EEPROM Get" + echo "=========================================================" + + ## CPU EEPROM + cat ${path_i2c_dev}/${i2c_bus}-$(printf "%04x" ${i2c_addr})/eeprom | hexdump -v -C +} + +#Set FAN Tray LED +function _i2c_fan_tray_led { + local value=0 + + echo "=========================================================" + echo "# Description: Set Fan Tray LED" + echo "=========================================================" + + case ${FAN_TRAY} in + 1) + ioPort=$REG_OUT_0 + if [ "${COLOR_LED}" == "green" ]; then + mask=${BIT_MASK[0]} + elif [ "${COLOR_LED}" == "amber" ]; then + mask=${BIT_MASK[1]} + fi + ;; + 2) + ioPort=$REG_OUT_0 + if [ "${COLOR_LED}" == "green" ]; then + mask=${BIT_MASK[4]} + elif [ "${COLOR_LED}" == "amber" ]; then + mask=${BIT_MASK[5]} + fi + ;; + 3) + ioPort=$REG_OUT_1 + if [ "${COLOR_LED}" == "green" ]; then + mask=${BIT_MASK[0]} + elif [ "${COLOR_LED}" == "amber" ]; then + mask=${BIT_MASK[1]} + fi + ;; + 4) + ioPort=$REG_OUT_1 + if [ "${COLOR_LED}" == "green" ]; then + mask=${BIT_MASK[4]} + elif [ "${COLOR_LED}" == "amber" ]; then + mask=${BIT_MASK[5]} + fi + ;; + *) + echo "Please input 1~4" + exit + ;; + esac + + if [ "${ONOFF_LED}" == "on" ]; then + value=0xFF + elif [ "${ONOFF_LED}" == "off" ]; then + value=0x00 + else + echo "Invalid Parameters ${ONOFF_LED}, Exit!!!" + _help + exit ${FALSE} + fi + i2cset -m $mask -y -r ${I2C_BUS_FAN_BOARD} ${I2C_ADDR_FAN_BOARD} $ioPort $value + +} + +#Set System Status LED +function _i2c_sys_led { + local value=0 + local mask=${BIT_MASK[0]} + + if [ "${COLOR_LED}" == "green" ]; then + value=0xFF + elif [ "${COLOR_LED}" == "amber" ]; then + value=0x00 + else + echo "Invalid Parameters ${COLOR_LED}, Exit!!!" + _help + exit ${FALSE} + fi + + _i2c_set_led $value $mask +} + +#Set FAN LED +function _i2c_fan_led { + local value=0 + local mask=${BIT_MASK[2]} + + if [ "${COLOR_LED}" == "green" ]; then + value=0x00 + elif [ "${COLOR_LED}" == "amber" ]; then + value=0xFF + else + echo "Invalid Parameters ${COLOR_LED}, Exit!!!" + _help + exit ${FALSE} + fi + + _i2c_set_led $value $mask +} + +#Set PSU1 LED +function _i2c_psu1_led { + local value=0 + local mask=${BIT_MASK[3]} + + if [ "${COLOR_LED}" == "green" ]; then + value=0x00 + elif [ "${COLOR_LED}" == "amber" ]; then + value=0xFF + else + echo "Invalid Parameters ${COLOR_LED}, Exit!!!" + _help + exit ${FALSE} + fi + + _i2c_set_led $value $mask +} + +#Set PSU2 LED +function _i2c_psu2_led { + local reg=${REG_OUT_0} + local value=0 + local mask=${BIT_MASK[4]} + + if [ "${COLOR_LED}" == "green" ]; then + value=0x00 + elif [ "${COLOR_LED}" == "amber" ]; then + value=0xFF + else + echo "Invalid Parameters ${COLOR_LED}, Exit!!!" + _help + exit ${FALSE} + fi + + _i2c_set_led $value $mask +} + +#Set LEDs in LED Board +function _i2c_set_led { + local value=$1 + local mask=$2 + local reg=${REG_OUT_0} + local i2c_bus=$I2C_BUS_LED_BOARD + local i2c_addr=$I2C_ADDR_LED_BOARD + + _i2c_set $i2c_bus $i2c_addr $reg $mask $value +} + +#Reset all system leds +function _i2c_reset_led { + local mask=0xFF + local value=$((2#00000011)) + local reg=${REG_OUT_0} + local i2c_bus=$I2C_BUS_LED_BOARD + local i2c_addr=$I2C_ADDR_LED_BOARD + + _i2c_set $i2c_bus $i2c_addr $reg $mask $value +} + +#Get Mother Board Version and Type +function _i2c_board_type_get { + echo "=========================================================" + echo "# Description: I2C MB Board Type Get" + echo "=========================================================" + local i2c_bus=${I2C_BUS_ID_GPIO} + local i2c_addr=${I2C_ADDR_ID_GPIO} + local reg=${REG_IN_1} + local value=0 + + value=$(_i2c_get $i2c_bus $i2c_addr $reg) + + #Bit 0-1 + boardBuildRev=$((value & 0x03)) + #Bit 2-3 + boardHwRev=$((value >> 2 & 0x03)) + #Bit 4-7 + boardId=$((value>>4)) + printf "[MB BOARD_ID] = 0x%02x, [HW Rev] = %d, [Build Rev] = %d\n" $boardId $boardHwRev $boardBuildRev +} + +#Get Board Version and Type from Dummy BMC Board +function _i2c_bmc_board_type_get { + echo "=========================================================" + echo "# Description: I2C BMC Board Type Get" + echo "=========================================================" + + local i2c_bus=${I2C_BUS_BMC_BOARD_TYPE} + local i2c_addr=${I2C_ADDR_BMC_BOARD_TYPE} + local reg=${REG_IN_1} + local value=0 + + value=$(_i2c_get $i2c_bus $i2c_addr $reg) + + #Bit 0-1 + boardBuildRev=$((($value) & 0x03)) + #Bit 2-3 + boardHwRev=$((($value) >> 2 & 0x03)) + #Bit 4-7 + boardId=$((($value) >> 4)) + printf "[BMC BOARD_ID] = 0x%02x, [HW Rev] = %d, [Build Rev] = %d\n" $boardId $boardHwRev $boardBuildRev +} + +#Get CPLD Version +function _i2c_cpld_version { + echo "=========================================================" + echo "# Description: CPLD Version" + echo "=========================================================" + cat ${PATH_SYS_CPLD}/cpld_version +} + +#Get PSU Status +function _i2c_psu_status { + local psu_abs="" + + psu1PwGood=`cat ${PATH_SYSFS_PSU1}/psu_pg` + psu_abs=`cat ${PATH_SYSFS_PSU1}/psu_abs` + if [ "$psu_abs" == "0" ]; then + psu1Exist=1 + else + psu1Exist=0 + fi + + psu2PwGood=`cat ${PATH_SYSFS_PSU2}/psu_pg` + psu_abs=`cat ${PATH_SYSFS_PSU2}/psu_abs` + if [ "$psu_abs" == "0" ]; then + psu2Exist=1 + else + psu2Exist=0 + fi + + printf "PSU1 Exist:%x PSU1 PW Good:%d\n" $psu1Exist $psu1PwGood + printf "PSU2 Exist:%d PSU2 PW Good:%d\n" $psu2Exist $psu2PwGood +} + +#Set 10G Mux to CPU or Front Panel +function _i2c_10g_mux { + + echo "=========================================================" + echo "# Description: 10G Mux" + echo "=========================================================" + + local target=${TARGET_10G_MUX} + local i2c_bus=${I2C_BUS_10GMUX} + local i2c_addr=${I2C_ADDR_10GMUX} + local reg=0 + local mask=0xff + local value=0 + local file_10g_mux="${PATH_SYS_CPLD}/cpld_10g_mux" + + #get current 10g mux register value on CPLD + value=`cat ${file_10g_mux}` + + if [ "${target}" == "cpu" ]; then + #Set XE0: slave address access + _i2c_set $i2c_bus $i2c_addr 0x06 $mask 0x18 + #Set XE0: CH4 D_IN0-S_OUTA0 EQ + _i2c_set $i2c_bus $i2c_addr 0x2c $mask 0x00 + #Set XE0: CH5 NC-S_OUTB0 DEM + _i2c_set $i2c_bus $i2c_addr 0x35 $mask 0x80 + #Set XE0: CH5 NC-S_OUTB0 VOD + _i2c_set $i2c_bus $i2c_addr 0x34 $mask 0xab + #Set XE0: CH1 D_OUT0-S_INB0 EQ + _i2c_set $i2c_bus $i2c_addr 0x16 $mask 0x01 + #Set XE0: CH1 D_OUT0-S_INB0 DEM + _i2c_set $i2c_bus $i2c_addr 0x18 $mask 0x80 + #Set XE0: CH1 D_OUT0-S_INB0 VOD + _i2c_set $i2c_bus $i2c_addr 0x17 $mask 0xab + #Set XE1: CH6 D_IN1-S_OUTA1 EQ + _i2c_set $i2c_bus $i2c_addr 0x3a $mask 0x00 + #Set XE1: CH7 NC-S_OUTB1 EQ + _i2c_set $i2c_bus $i2c_addr 0x41 $mask 0x00 + #Set XE1: CH7 NC-S_OUTB1 DEM + _i2c_set $i2c_bus $i2c_addr 0x43 $mask 0x80 + #Set XE1: CH7 NC-S_OUTB1 VOD + _i2c_set $i2c_bus $i2c_addr 0x42 $mask 0xab + #Set XE1: CH3 D_OUT1-S_INB1 EQ + _i2c_set $i2c_bus $i2c_addr 0x24 $mask 0x01 + #Set XE1: CH3 D_OUT1-S_INB1 DEM + _i2c_set $i2c_bus $i2c_addr 0x26 $mask 0x80 + #Set XE1: CH3 D_OUT1-S_INB1 VOD + _i2c_set $i2c_bus $i2c_addr 0x25 $mask 0xab + #Enable auto negotiation of XE ports + eval "bcmcmd 'port xe0,xe1 an=1 if=kr en=1'" + #delay 1 sec + sleep 1 + + #switch 10G MUX to cpu on CPLD + value=$(( value & 0xf3 )) + echo "$(printf "%x" ${value})" > ${file_10g_mux} + + echo "Switch 10G Mux to [CPU]" + + elif [ "${COLOR_LED}" == "fp" ]; then + #Set XE0: slave address access + _i2c_set $i2c_bus $i2c_addr 0x06 $mask 0x18 + #Set XE0: CH4 D_IN0-S_OUTA0 EQ + _i2c_set $i2c_bus $i2c_addr 0x2c $mask 0x00 + #Set XE0: CH4 D_IN0-S_OUTA0 DEM + _i2c_set $i2c_bus $i2c_addr 0x2e $mask 0x80 + #Set XE0: CH4 D_IN0-S_OUTA0 VOD + _i2c_set $i2c_bus $i2c_addr 0x2d $mask 0xab + #Set XE0: CH0 NC-S_INA0 EQ + _i2c_set $i2c_bus $i2c_addr 0x0f $mask 0x00 + #Set XE0: CH1 D_OUT0-S_INB0 DEM + _i2c_set $i2c_bus $i2c_addr 0x18 $mask 0x80 + #Set XE0: CH1 D_OUT0-S_INB0 VOD + _i2c_set $i2c_bus $i2c_addr 0x17 $mask 0xab + #Set XE1: CH6 D_IN1-S_OUTA1 EQ + _i2c_set $i2c_bus $i2c_addr 0x3a $mask 0x00 + #Set XE1: CH6 D_IN1-S_OUTA1 DEM + _i2c_set $i2c_bus $i2c_addr 0x3c $mask 0x80 + #Set XE1: CH6 D_IN1-S_OUTA1 VOD + _i2c_set $i2c_bus $i2c_addr 0x3b $mask 0xab + #Set XE1: CH2 NC-S_INA1 EQ + _i2c_set $i2c_bus $i2c_addr 0x1d $mask 0x00 + #Set XE1: CH3 D_OUT1-S_INB1 DEM + _i2c_set $i2c_bus $i2c_addr 0x26 $mask 0x80 + #Set XE1: CH3 D_OUT1-S_INB1 VOD + _i2c_set $i2c_bus $i2c_addr 0x25 $mask 0xab + #Enable auto negotiation of XE ports + eval "bcmcmd 'port xe0,xe1 an=0 if=kr en=1'" + #delay 1 sec + sleep 1 + + #switch 10G MUX to front panel on CPLD + value=$(( value | 0x0c )) + echo "$(printf "%x" ${value})" > ${file_10g_mux} + + echo "Switch 10G Mux to [Front Panel]" + else + echo "invalid target, please set target to cpu/fp" + fi +} + +#Increase read socket buffer for CoPP Test +function _config_rmem { + echo "109430400" > /proc/sys/net/core/rmem_max +} + +#Main Function +function _main { + start_time_str=`date` + start_time_sec=$(date +%s) + + if [ "${EXEC_FUNC}" == "help" ]; then + _help + elif [ "${EXEC_FUNC}" == "i2c_init" ]; then + _i2c_init + elif [ "${EXEC_FUNC}" == "i2c_deinit" ]; then + _i2c_deinit + elif [ "${EXEC_FUNC}" == "i2c_temp_init" ]; then + _i2c_temp_init + elif [ "${EXEC_FUNC}" == "i2c_fan_init" ]; then + _i2c_fan_init + elif [ "${EXEC_FUNC}" == "i2c_volmon_init" ]; then + _i2c_volmon_init + elif [ "${EXEC_FUNC}" == "i2c_io_exp_init" ]; then + _i2c_io_exp_init + elif [ "${EXEC_FUNC}" == "i2c_led_test" ]; then + _i2c_led_test + elif [ "${EXEC_FUNC}" == "i2c_mb_eeprom_get" ]; then + _i2c_mb_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_cpu_eeprom_get" ]; then + _i2c_cpu_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_psu_eeprom_get" ]; then + _i2c_psu_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_eeprom_get" ]; then + _i2c_qsfp_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_eeprom_init" ]; then + _i2c_qsfp_eeprom_init ${QSFP_INIT_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_mb_eeprom_init" ]; then + _i2c_mb_eeprom_init ${MB_EEPROM_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_cpu_eeprom_init" ]; then + _i2c_cpu_eeprom_init ${MB_EEPROM_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_qsfp_status_get" ]; then + _i2c_qsfp_status_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_type_get" ]; then + _i2c_qsfp_type_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_signal_get" ]; then + _i2c_qsfp_signal_get ${QSFP_PORT} ${QSFP_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_qsfp_signal_set" ]; then + _i2c_qsfp_signal_set ${QSFP_PORT} ${QSFP_ACTION} ${QSFP_VALUE} + elif [ "${EXEC_FUNC}" == "i2c_led_psu_status_set" ]; then + _i2c_led_psu_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_status_set" ]; then + _i2c_led_fan_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_tray_status_set" ]; then + _i2c_led_fan_tray_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_tray_test" ]; then + _i2c_led_fan_tray_test + elif [ "${EXEC_FUNC}" == "i2c_sys_led" ]; then + _i2c_sys_led + elif [ "${EXEC_FUNC}" == "i2c_fan_led" ]; then + _i2c_fan_led + elif [ "${EXEC_FUNC}" == "i2c_fan_tray_led" ]; then + _i2c_fan_tray_led + elif [ "${EXEC_FUNC}" == "i2c_psu1_led" ]; then + _i2c_psu1_led + elif [ "${EXEC_FUNC}" == "i2c_psu2_led" ]; then + _i2c_psu2_led + elif [ "${EXEC_FUNC}" == "i2c_board_type_get" ]; then + _i2c_board_type_get + elif [ "${EXEC_FUNC}" == "i2c_bmc_board_type_get" ]; then + _i2c_bmc_board_type_get + elif [ "${EXEC_FUNC}" == "i2c_cpld_version" ]; then + _i2c_cpld_version + elif [ "${EXEC_FUNC}" == "i2c_psu_status" ]; then + _i2c_psu_status + elif [ "${EXEC_FUNC}" == "i2c_test_all" ]; then + _i2c_deinit + _i2c_init + _i2c_led_test + _i2c_psu_eeprom_get + _i2c_mb_eeprom_get + _i2c_cpu_eeprom_get + _i2c_board_type_get + _i2c_bmc_board_type_get + _i2c_cpld_version + _i2c_psu_status + elif [ "${EXEC_FUNC}" == "i2c_10g_mux" ]; then + _i2c_10g_mux + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + end_time_str=`date` + end_time_sec=$(date +%s) + diff_time=$[ ${end_time_sec} - ${start_time_sec} ] + echo "------------------------------" + echo "Total Execution Time: ${diff_time} sec" + + echo "DONE" +} + +_main diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/utils/qsfp_monitor.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/utils/qsfp_monitor.sh new file mode 100755 index 000000000000..51c49c1152f5 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/utils/qsfp_monitor.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# Copyright (C) 2017 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=3 +I2C_UTILS="/usr/sbin/i2c_utils.sh" +QSFP_SI_SCRIPT="/usr/sbin/qsfp_si_cfg.sh" +QSFP_ARRAY=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \ + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + +#QSFP SI monitor +function _qsfp_si_monitor { + local i + local status + for i in {0..63}; + do + status=`${I2C_UTILS} i2c_qsfp_status_get $(expr $i + 1) | egrep '^status=.*$' | sed -e 's/status=//g'` + if [ "${status}" == "1" ]; then + _qsfp_type_check $i + fi + done +} + +#QSFP type +function _qsfp_type_check { + local port=$1 + local qsfp_type=`${I2C_UTILS} i2c_qsfp_type_get $(expr $port + 1) ` + local identifier=`echo "$qsfp_type" | egrep -o 'identifier = .*$' | sed -e 's/identifier = //g'` + if [ "${identifier}" == "11" ]; then + connector=`echo "$qsfp_type" | egrep -o 'connector = .*$' | sed -e 's/connector = //g'` + case ${connector} in + 21|23) + #DAC + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to DAC" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} dac $port >/dev/null + fi + ;; + *) + #Optical + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to Optical" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} optical $port >/dev/null + fi + ;; + esac + fi +} + +#Docker exist check +function _docker_swss_check { + while true + do + # Check if syncd starts + result=`docker exec -i swss bash -c "echo -en \"SELECT 1\\nHLEN HIDDEN\" | redis-cli | sed -n 2p"` #TBD FIX ME + if [ "$result" == "3" ]; then + return + fi + sleep $INTERVAL + done +} + +#Docker exist check +function _qsfp_si_cfg_script_check { + + if [ -f ${QSFP_SI_SCRIPT} ] && [ -x ${QSFP_SI_SCRIPT} ]; then + echo "SI Script exists. Start monitor." + return + else + echo "SI Script not exist. Exit monitor." + exit + fi +} + +# main function +function _main { + #Check SI Script + _qsfp_si_cfg_script_check + #Check docker swss is running + _docker_swss_check + while true + do + _qsfp_si_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/utils/qsfp_si_cfg.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/utils/qsfp_si_cfg.sh new file mode 100755 index 000000000000..ba59c50ca7bf --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/utils/qsfp_si_cfg.sh @@ -0,0 +1,230 @@ +#!/bin/bash +# Copyright (C) 2017 Ingrasys, Inc. +# +# 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 3 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, see . + +VERSION="1.0.0" +TRUE=200 +FALSE=404 + +TYPE=${1} +PORT=${2} + +#FIXME update SI values +#QSFP Optical +function _qsfp_optical_si_set { + local idx=0 + #FIXME + local si=(11 68 13 15 68 15 11 68 9 11 68 13 + 11 68 17 11 68 13 13 68 11 11 68 15 + 11 68 13 11 68 17 11 68 17 13 68 13 + 11 68 15 11 68 9 11 68 9 11 68 7 + 9 68 13 13 68 7 11 68 7 11 68 11 + 11 68 13 11 68 13 9 68 7 11 68 11 + 11 68 9 11 68 15 11 68 9 11 68 9 + 11 68 9 11 68 7 11 68 9 11 68 9 + 13 68 9 13 68 13 13 68 7 13 68 11 + 13 68 5 13 68 11 13 68 5 13 68 11 + + 11 68 3 11 68 9 11 68 5 11 68 13 + 11 68 9 11 68 9 11 68 11 13 68 5 + 13 68 3 11 68 7 11 68 5 11 68 9 + 11 68 9 13 68 9 13 68 1 11 68 11 + 5 68 1 11 68 7 11 68 1 13 68 3 + 11 68 5 11 68 3 11 68 7 11 68 3 + 11 68 3 13 68 11 13 68 1 13 68 3 + 11 68 1 13 68 1 13 68 1 11 68 7 + 11 68 1 11 68 5 11 68 1 9 68 5 + 5 68 1 13 68 1 11 68 5 11 68 3 + + 11 68 1 11 68 11 11 68 1 13 68 7 + 13 68 1 15 68 1 11 68 1 13 68 7 + 13 68 1 11 68 5 11 68 1 11 68 1 + 11 68 5 13 68 3 11 68 5 9 68 1 + 11 68 5 13 68 5 13 68 1 11 68 9 + 13 68 1 11 68 13 13 68 1 11 68 7 + 11 68 1 11 68 1 9 68 1 11 68 1 + 11 68 1 11 68 1 11 68 5 11 68 1 + 13 68 1 11 68 11 11 68 1 11 68 1 + 11 68 1 11 68 15 11 68 1 13 68 5 + + 11 68 1 11 68 1 9 68 1 13 68 1 + 11 68 1 11 68 1 9 68 1 11 68 1 + 11 68 1 13 68 3 11 68 1 13 68 1 + 11 68 1 13 68 1 11 68 1 11 68 13 + 11 68 1 11 68 1 11 68 1 11 68 1 + 11 68 11 11 68 1 13 68 1 11 68 1 + 11 68 1 11 68 9 11 68 1 11 68 7 + 11 68 1 13 68 1 13 68 1 11 68 1 + 11 68 1 11 68 5 11 68 1 13 68 3 + 11 68 3 9 68 1 11 68 3 11 68 1 + + 11 68 7 11 68 9 11 68 1 11 68 9 + 11 68 3 11 68 15 11 68 3 9 68 9 + 9 68 1 11 68 3 11 68 1 11 68 5 + 11 68 5 5 68 5 11 68 5 11 68 1 + 13 68 5 13 68 5 11 68 1 11 68 9 + 11 68 1 11 68 1 11 68 1 13 68 7 + 11 68 1 11 68 5 11 68 1 11 68 3 + 11 68 5 11 68 1 11 68 5 11 68 1 + 11 68 3 11 68 7 11 68 3 11 68 9 + 11 68 1 13 68 7 11 68 1 9 68 9 + + 13 68 1 11 68 5 11 68 3 13 68 5 + 11 68 7 11 68 11 13 68 5 9 68 5 + 11 68 5 11 68 13 13 68 7 11 68 13 + 11 68 7 11 68 11 11 68 5 11 68 11 + 11 68 11 11 68 11 11 68 7 11 68 11 + 11 68 13 11 68 11 11 68 9 15 68 9 + 11 68 11 11 68 5 11 68 7 13 68 11 + 11 68 9 13 68 17 11 68 7 11 68 13 + 11 68 11 11 68 17 11 68 13 11 68 15 + 13 68 9 13 68 11 11 68 11 13 68 7 + + 11 68 13 13 68 9 11 68 9 11 68 9 + 11 68 19 11 68 15 11 68 9 11 68 11 + 13 68 13 13 68 17 13 68 11 11 68 11 + 13 68 9 13 68 13 11 68 11 11 68 9) + + idx=$(( PORT * 12 )) + #FIXEME + #for j in {0..3} + #do + # bcmcmd "phy ce${PORT} CL93N72_UT_CTL2r.${j} CL93N72_TXFIR_POST=${si[idx++]}; phy ce${PORT} CL93N72_UT_CTL3r.${j} CL93N72_TXFIR_MAIN=${si[idx++]}; phy ce${PORT} CL93N72_UT_CTL2r.${j} CL93N72_TXFIR_PRE=${si[idx++]};" + #done +} + +function _qsfp_dac_si_set { + local idx=0 + # each port has 4 lanes, each lane has 3 SI values (post,main,pre), total 4*3=12 + local port_si=12 + + #si[0]: txfir_post for port0 lane0 + #si[1]: txfir_main for port0 lane0 + #si[2]: txfir_pre for port0 lane0 + + #si[3]: txfir_post for port0 lane1 + #si[4]: txfir_main for port0 lane1 + #si[5]: txfir_pre for port0 lane1 + + local si=(11 68 13 15 68 15 11 68 9 11 68 13 + 11 68 17 11 68 13 13 68 11 11 68 15 + 11 68 13 11 68 17 11 68 17 13 68 13 + 11 68 15 11 68 9 11 68 9 11 68 7 + 9 68 13 13 68 7 11 68 7 11 68 11 + 11 68 13 11 68 13 9 68 7 11 68 11 + 11 68 9 11 68 15 11 68 9 11 68 9 + 11 68 9 11 68 7 11 68 9 11 68 9 + 13 68 9 13 68 13 13 68 7 13 68 11 + 13 68 5 13 68 11 13 68 5 13 68 11 + + 11 68 3 11 68 9 11 68 5 11 68 13 + 11 68 9 11 68 9 11 68 11 13 68 5 + 13 68 3 11 68 7 11 68 5 11 68 9 + 11 68 9 13 68 9 13 68 1 11 68 11 + 5 68 1 11 68 7 11 68 1 13 68 3 + 11 68 5 11 68 3 11 68 7 11 68 3 + 11 68 3 13 68 11 13 68 1 13 68 3 + 11 68 1 13 68 1 13 68 1 11 68 7 + 11 68 1 11 68 5 11 68 1 9 68 5 + 5 68 1 13 68 1 11 68 5 11 68 3 + + 11 68 1 11 68 11 11 68 1 13 68 7 + 13 68 1 15 68 1 11 68 1 13 68 7 + 13 68 1 11 68 5 11 68 1 11 68 1 + 11 68 5 13 68 3 11 68 5 9 68 1 + 11 68 5 13 68 5 13 68 1 11 68 9 + 13 68 1 11 68 13 13 68 1 11 68 7 + 11 68 1 11 68 1 9 68 1 11 68 1 + 11 68 1 11 68 1 11 68 5 11 68 1 + 13 68 1 11 68 11 11 68 1 11 68 1 + 11 68 1 11 68 15 11 68 1 13 68 5 + + 11 68 1 11 68 1 9 68 1 13 68 1 + 11 68 1 11 68 1 9 68 1 11 68 1 + 11 68 1 13 68 3 11 68 1 13 68 1 + 11 68 1 13 68 1 11 68 1 11 68 13 + 11 68 1 11 68 1 11 68 1 11 68 1 + 11 68 11 11 68 1 13 68 1 11 68 1 + 11 68 1 11 68 9 11 68 1 11 68 7 + 11 68 1 13 68 1 13 68 1 11 68 1 + 11 68 1 11 68 5 11 68 1 13 68 3 + 11 68 3 9 68 1 11 68 3 11 68 1 + + 11 68 7 11 68 9 11 68 1 11 68 9 + 11 68 3 11 68 15 11 68 3 9 68 9 + 9 68 1 11 68 3 11 68 1 11 68 5 + 11 68 5 5 68 5 11 68 5 11 68 1 + 13 68 5 13 68 5 11 68 1 11 68 9 + 11 68 1 11 68 1 11 68 1 13 68 7 + 11 68 1 11 68 5 11 68 1 11 68 3 + 11 68 5 11 68 1 11 68 5 11 68 1 + 11 68 3 11 68 7 11 68 3 11 68 9 + 11 68 1 13 68 7 11 68 1 9 68 9 + + 13 68 1 11 68 5 11 68 3 13 68 5 + 11 68 7 11 68 11 13 68 5 9 68 5 + 11 68 5 11 68 13 13 68 7 11 68 13 + 11 68 7 11 68 11 11 68 5 11 68 11 + 11 68 11 11 68 11 11 68 7 11 68 11 + 11 68 13 11 68 11 11 68 9 15 68 9 + 11 68 11 11 68 5 11 68 7 13 68 11 + 11 68 9 13 68 17 11 68 7 11 68 13 + 11 68 11 11 68 17 11 68 13 11 68 15 + 13 68 9 13 68 11 11 68 11 13 68 7 + + 11 68 13 13 68 9 11 68 9 11 68 9 + 11 68 19 11 68 15 11 68 9 11 68 11 + 13 68 13 13 68 17 13 68 11 11 68 11 + 13 68 9 13 68 13 11 68 11 11 68 9) + + # get first si index for this port + idx=$(( PORT * port_si )) + + # loop for 4 lanes + for j in {0..3} + do + bcmcmd "phy ce${PORT} CL93N72_UT_CTL2r.${j} CL93N72_TXFIR_POST=${si[idx++]}; phy ce${PORT} CL93N72_UT_CTL3r.${j} CL93N72_TXFIR_MAIN=${si[idx++]}; phy ce${PORT} CL93N72_UT_CTL2r.${j} CL93N72_TXFIR_PRE=${si[idx++]};" + done +} + +# Help usage function +function _help { + echo "=========================================================" + echo "# Description: Help Function" + echo "=========================================================" + echo "----------------------------------------------------" + echo "EX : ${0} help" + echo " : ${0} optical [0-63]" + echo " : ${0} dac [0-63]" + echo "----------------------------------------------------" +} + +#Main Function +function _main { + + if [ "${TYPE}" == "help" ]; then + _help + elif [ "${TYPE}" == "optical" ]; then + _qsfp_optical_si_set + elif [ "${TYPE}" == "dac" ]; then + _qsfp_dac_si_set + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi +} + +_main diff --git a/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/utils/s9200_64x_monitor.sh b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/utils/s9200_64x_monitor.sh new file mode 100755 index 000000000000..974da6d5001f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-ingrasys/s9200-64x/utils/s9200_64x_monitor.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=5 +I2C_UTILS="/usr/sbin/i2c_utils.sh" + + +# LED status monitor +function _led_monitor { + ${I2C_UTILS} i2c_led_fan_status_set >/dev/null + ${I2C_UTILS} i2c_led_psu_status_set >/dev/null + ${I2C_UTILS} i2c_led_fan_tray_status_set >/dev/null +} + +# main function +function _main { + while true + do + _led_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-mitac b/platform/broadcom/sonic-platform-modules-mitac deleted file mode 160000 index 5b1ce26b7688..000000000000 --- a/platform/broadcom/sonic-platform-modules-mitac +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5b1ce26b76880979de5bbbb54140a750b2809367 diff --git a/platform/broadcom/sonic-platform-modules-mitac/.gitignore b/platform/broadcom/sonic-platform-modules-mitac/.gitignore new file mode 100644 index 000000000000..38e72f05332d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/.gitignore @@ -0,0 +1,17 @@ +# Build file +ly1200-32x/modules/*.mod.c +ly1200-32x/modules/*.mod.o +ly1200-32x/modules/*.o +ly1200-32x/modules/*.ko +ly1200-32x/modules/*.cmd +ly1200-32x/modules/Module.symvers +ly1200-32x/modules/modules.order +ly1200-32x/modules/.tmp_versions + +debian/sonic-platform-mitac-ly1200-32x/ +debian/sonic-platform-mitac-ly1200-32x.debhelper.log +debian/sonic-platform-mitac-ly1200-32x.postinst.debhelper +debian/sonic-platform-mitac-ly1200-32x.postrm.debhelper +debian/sonic-platform-mitac-ly1200-32x.prerm.debhelper +debian/sonic-platform-mitac-ly1200-32x.substvars +debian/files diff --git a/platform/broadcom/sonic-platform-modules-mitac/LICENSE b/platform/broadcom/sonic-platform-modules-mitac/LICENSE new file mode 100644 index 000000000000..c07c63913aeb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/LICENSE @@ -0,0 +1,16 @@ +Copyright (C) 2016 Microsoft, Inc +Copyright (C) 2018 MiTAC Technology Corporation + +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. diff --git a/platform/broadcom/sonic-platform-modules-mitac/README.md b/platform/broadcom/sonic-platform-modules-mitac/README.md new file mode 100644 index 000000000000..d7d47a58debf --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/README.md @@ -0,0 +1 @@ +platform drivers of MiTAC products for the SONiC project diff --git a/platform/broadcom/sonic-platform-modules-mitac/debian/changelog b/platform/broadcom/sonic-platform-modules-mitac/debian/changelog new file mode 100644 index 000000000000..592bb3a71134 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/debian/changelog @@ -0,0 +1,5 @@ +sonic-mitac-platform-modules (1.0) unstable; urgency=low + + * Initial release + + -- MiTAC Network Thu, 01 Sep 2017 11:26:38 +0800 diff --git a/platform/broadcom/sonic-platform-modules-mitac/debian/compat b/platform/broadcom/sonic-platform-modules-mitac/debian/compat new file mode 100644 index 000000000000..ec635144f600 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/debian/compat @@ -0,0 +1 @@ +9 diff --git a/platform/broadcom/sonic-platform-modules-mitac/debian/control b/platform/broadcom/sonic-platform-modules-mitac/debian/control new file mode 100644 index 000000000000..8e20f8524d74 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/debian/control @@ -0,0 +1,12 @@ +Source: sonic-mitac-platform-modules +Section: main +Priority: extra +Maintainer: MiTAC network +Build-Depends: debhelper (>= 8.0.0), bzip2 +Standards-Version: 3.9.3 + +Package: sonic-platform-mitac-ly1200-32x +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as fan, led, sfp + diff --git a/platform/broadcom/sonic-platform-modules-mitac/debian/rules b/platform/broadcom/sonic-platform-modules-mitac/debian/rules new file mode 100755 index 000000000000..7f9f99827d21 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/debian/rules @@ -0,0 +1,41 @@ +#!/usr/bin/make -f + +export INSTALL_MOD_DIR:=extra + +PACKAGE_PRE_NAME := sonic-platform-mitac +KVERSION ?= $(shell uname -r) +KERNEL_SRC := /lib/modules/$(KVERSION) +MOD_SRC_DIR:= $(shell pwd) +MODULE_DIRS:= ly1200-32x + +%: + dh $@ + +override_dh_auto_build: + (for mod in $(MODULE_DIRS); do \ + make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + done) + +override_dh_auto_install: + (for mod in $(MODULE_DIRS); do \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod}\ + $(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin;\ + cp $(MOD_SRC_DIR)/$${mod}/modules/*.ko \ + debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + mkdir -p debian/$(PACKAGE_PRE_NAME)-$${mod}/opt; \ + cp -rfL $(MOD_SRC_DIR)/$${mod}/opt/* \ + debian/$(PACKAGE_PRE_NAME)-$${mod}/opt; \ + mkdir -p debian/$(PACKAGE_PRE_NAME)-$${mod}/etc/init.d; \ + cp -rfL $(MOD_SRC_DIR)/$${mod}/etc/* \ + debian/$(PACKAGE_PRE_NAME)-$${mod}/etc; \ + done) + +override_dh_usrlocal: + +override_dh_clean: + dh_clean + (for mod in $(MODULE_DIRS); do \ + make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules clean; \ + done) + diff --git a/platform/broadcom/sonic-platform-modules-mitac/debian/sonic-platform-mitac-ly1200-32x.init b/platform/broadcom/sonic-platform-modules-mitac/debian/sonic-platform-mitac-ly1200-32x.init new file mode 100644 index 000000000000..819b0287e8c3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/debian/sonic-platform-mitac-ly1200-32x.init @@ -0,0 +1,56 @@ +#!/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 ly1200-32x board. +### END INIT INFO + +case "$1" in +start) + eval sonic_version=$(cat /etc/sonic/sonic_version.yml | grep build_version | cut -f2 -d" ") + if [ -f /host/image-$sonic_version/platform/firsttime ]; then + update-rc.d xcvr_servd defaults >/dev/null + fi + + echo -n "Setting up board... " + + echo 0 > /proc/sys/kernel/perf_cpu_time_max_percent + + /etc/init.d/gpe start + /etc/init.d/i2c_init start + /etc/init.d/sys_polld start + /opt/script/start_watchdog.sh & + /opt/script/start_service.sh & + echo "done." + ;; + +stop) + echo -n "cleaning... " + + /etc/init.d/gpe stop + /etc/init.d/i2c_init stop + /etc/init.d/xcvr_servd stop + /etc/init.d/sys_servd stop + /etc/init.d/sys_polld stop + + echo "done." + + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/sonic-platform-mitac-ly1200-32x.init {start|stop}" + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-mitac/debian/sonic-platform-mitac-ly1200-32x.upstart b/platform/broadcom/sonic-platform-modules-mitac/debian/sonic-platform-mitac-ly1200-32x.upstart new file mode 100644 index 000000000000..39533b8226af --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/debian/sonic-platform-mitac-ly1200-32x.upstart @@ -0,0 +1,5 @@ +description "SONiC platform service" + +respawn + +exec /etc/init.d/sonic-platform-mitac-ly1200-32x start diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/fan-ctrld b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/fan-ctrld new file mode 100755 index 000000000000..936f01e8b640 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/fan-ctrld @@ -0,0 +1,35 @@ +#!/bin/bash +### BEGIN INIT INFO +# Provides: fan-ctrld +# Required-Start: +# Required-Stop: +# Default-Start: rc.local +# Default-Stop: +# Short-Description: Daemon fan-ctrld +### END INIT INFO + +N=/etc/init.d/fan-ctrld +D_PATH=/opt/fan-ctrl/fan-ctrl + +set -e + +stop_fan_ctrld () { + pkill -f $D_PATH +} + +case "$1" in + start) + $D_PATH & + ;; + stop) + stop_fan_ctrld + ;; + reload|restart|force-reload) + ;; + *) + echo "Usage: $N {start}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/gpe b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/gpe new file mode 100755 index 000000000000..c326442db047 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/gpe @@ -0,0 +1,39 @@ +#! /bin/bash +### BEGIN INIT INFO +# Provides: gpe +# Required-Start: +# Required-Stop: +# Default-Start: rc.local +# Default-Stop: +# Short-Description: Daemon gpe +### END INIT INFO + +# Load kernel modules +load_module () { + if [ `lsmod | grep -c "gpe "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/extra/mitac_ly1200_32x_gpe.ko + fi +} + +remove_module () { + rmmod mitac-ly1200-32x_gpe +} + +case "$1" in + start) + load_module + ;; + stop) + remove_module + ;; + reload|restart|force-reload) + remove_module + load_module + ;; + *) + echo "Usage: $N {start|stop|reload|restart|force-reload}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/i2c_init b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/i2c_init new file mode 100755 index 000000000000..1d0a94cbb4e6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/i2c_init @@ -0,0 +1,231 @@ +#!/bin/bash + +### BEGIN INIT INFO +# Provides: setup-board-i2c +# Required-Start: +# Required-Stop: +# Should-Start: +# Should-Stop: +# Default-Start: S +# Default-Stop: 0 6 +# Short-Description: Setup ly1200-32x board. +### END INIT INFO + +I2C_BUS_RM_ALL=0 +I2C_I801_RM_PRINT=0 +I2C_ISMT_RM_PRINT=0 +I2C_I801_INS_PRINT=0 +I2C_ISMT_INS_PRINT=0 +I2C_I801_RM_RETRY=0 +I2C_ISMT_RM_RETRY=0 +I2C_I801_INS_RETRY=0 +I2C_ISMT_INS_RETRY=0 +RETRY=3 + +# process name/id +DAEMON_NAME=`basename $0` +DAEMON_PID="$$" + +# default log file +DEF_LOG_FILE="/var/log/syslog" + +#/* +#* FEATURE: +#* log_msg +#* PURPOSE: +#* log message +#* PARAMETERS: +#* msg (IN) message +#* RETURNS: +#* +#*/ +function log_msg() { + local msg=$1 + + echo -e "`date +"%b %_d %T"` `hostname` $DAEMON_NAME[$DAEMON_PID]: ${msg}" >> ${DEF_LOG_FILE} +} + + +# Load kernel modules +load_modules () { + while [ 1 ] + do + if [ `lsmod | grep -c "i2c_i801 "` -eq 1 ] && [ `lsmod | grep -c "i2c_ismt "` -eq 1 ]; then + log_msg "The kernel module i801 and ismt was insert." + break; + fi + if [ $I2C_BUS_RM_ALL -eq 0 ]; then + I2C_BUS_RM_ALL=1 + log_msg "Wait for i2c_i801 and i2c_ismt module insert." + fi + sleep 1 + done + if [ $I2C_BUS_RM_ALL -eq 1 ]; then + log_msg "i2c_i801 and i2c_ismt auto insert success." + fi + #----------remove i801 start--------------------- + if [ `lsmod | grep -c "i2c_i801 "` -eq 1 ]; then + rmmod i2c_i801 + while [ `lsmod | grep -c "i2c_i801 "` -eq 1 ] + do + if [ $I2C_I801_RM_PRINT -eq 0 ]; then + I2C_I801_RM_PRINT=1 + log_msg "Wait for i2c_i801 remove." + fi + if [ $I2C_I801_RM_RETRY -lt $RETRY ];then + I2C_I801_RM_RETRY=$((I2C_I801_RM_RETRY + 1)) + else + break + fi + sleep 1 + done + if [ $I2C_I801_RM_PRINT -eq 1 ]; then + log_msg "i2c_i801 remove success." + fi + fi + #----------remove i801 end------------------------ + + + #----------remove ismt start---------------------- + if [ `lsmod | grep -c "i2c_ismt "` -eq 1 ]; then + rmmod i2c_ismt + while [ `lsmod | grep -c "i2c_ismt "` -eq 1 ] + do + if [ $I2C_ISMT_RM_PRINT -eq 0 ]; then + I2C_ISMT_RM_PRINT=1 + log_msg "Wait for i2c_ismt remove." + fi + if [ $I2C_ISMT_RM_RETRY -lt $RETRY ];then + I2C_ISMT_RM_RETRY=$((I2C_ISMT_RM_RETRY + 1)) + else + break + fi + sleep 1 + done + if [ $I2C_ISMT_RM_PRINT -eq 1 ]; then + log_msg "i2c_ismt remove success." + fi + fi + #----------remove ismt end------------------------ + + #----------insert i801 start---------------------- + insmod /lib/modules/`uname -r`/kernel/drivers/i2c/busses/i2c-i801.ko + while [ `lsmod | grep -c "i2c_i801 "` -eq 0 ] + do + if [ $I2C_I801_INS_PRINT -eq 0 ]; then + I2C_I801_INS_PRINT=1 + log_msg "Wait for i2c_i801 insert." + fi + if [ $I2C_I801_INS_RETRY -lt $RETRY ];then + I2C_I801_INS_RETRY=$((I2C_I801_INS_RETRY + 1)) + else + break + fi + sleep 1 + done + if [ $I2C_I801_INS_PRINT -eq 1 ]; then + log_msg "i2c_i801 insert success." + fi + #----------insert i801 end------------------------ + + #----------insert ismt start---------------------- + insmod /lib/modules/`uname -r`/kernel/drivers/i2c/busses/i2c-ismt.ko + while [ `lsmod | grep -c "i2c_ismt "` -eq 0 ] + do + if [ $I2C_ISMT_INS_PRINT -eq 0 ]; then + I2C_ISMT_INS_PRINT=1 + log_msg "Wait for i2c_ismt insert." + fi + if [ $I2C_ISMT_INS_RETRY -lt $RETRY ];then + I2C_ISMT_INS_RETRY=$((I2C_ISMT_INS_RETRY + 1)) + else + break + fi + sleep 1 + done + if [ $I2C_ISMT_INS_PRINT -eq 1 ]; then + log_msg "i2c_ismt insert success." + fi + #----------insert ismt start------------------------ + if [ `lsmod | grep -c "at24 "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/kernel/drivers/misc/eeprom/at24.ko + fi + if [ `lsmod | grep -c "i2c_mux "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/kernel/drivers/i2c/i2c-mux.ko + fi + if [ `lsmod | grep -c "i2c_mux_pca954x "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/kernel/drivers/i2c/muxes/i2c-mux-pca954x.ko + fi + if [ `lsmod | grep -c "lm75 "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/kernel/drivers/hwmon/lm75.ko + fi + if [ `lsmod | grep -c "max31790 "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/kernel/drivers/hwmon/max31790.ko + fi + if [ `lsmod | grep -c "sff_8436_eeprom "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/kernel/drivers/misc/eeprom/sff_8436_eeprom.ko + fi + if [ `lsmod | grep -c "mitac_ly1200_32x_fse000 "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/extra/mitac_ly1200_32x_fse000.ko + fi + if [ `lsmod | grep -c "mitac_ly1200_32x_system_cpld "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/extra/mitac_ly1200_32x_system_cpld.ko + fi + if [ `lsmod | grep -c "mitac_ly1200_32x_master_cpld "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/extra/mitac_ly1200_32x_master_cpld.ko + fi + if [ `lsmod | grep -c "mitac_ly1200_32x_slave_cpld "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/extra/mitac_ly1200_32x_slave_cpld.ko + fi + if [ `lsmod | grep -c "mitac_ly1200_32x_cb_i2c "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/extra/mitac_ly1200_32x_cb_i2c.ko + fi + if [ `lsmod | grep -c "mitac_ly1200_32x_sb_i2c "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/extra/mitac_ly1200_32x_sb_i2c.ko + fi + if [ `lsmod | grep -c "mitac_ly1200_32x_pb_i2c "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/extra/mitac_ly1200_32x_pb_i2c.ko + fi + if [ `lsmod | grep -c "mitac_ly1200_32x_fb_i2c "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/extra/mitac_ly1200_32x_fb_i2c.ko + fi + if [ `lsmod | grep -c "mitac_ly1200_32x_fb_module_i2c "` -eq 0 ]; then + insmod /lib/modules/`uname -r`/extra/mitac_ly1200_32x_fb_module_i2c.ko + fi +} + +# Load i2c_dev modules +load_i2c_dev_modules () { + if [ `lsmod | grep -c "i2c_dev "` -eq 0 ]; then + modprobe i2c_dev + fi +} + +case "$1" in +start) + echo -n "Setting up board... " + depmod -a + load_i2c_dev_modules + load_modules + echo "done." + ;; + +stop) + echo -n "cleaning... " + + + echo "done." + + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/i2c_init {start|stop}" + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/sys_polld b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/sys_polld new file mode 100755 index 000000000000..1df43346f257 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/sys_polld @@ -0,0 +1,48 @@ +#!/bin/bash +### BEGIN INIT INFO +# Provides: sys-polld +# Required-Start: +# Required-Stop: +# Default-Start: rc.local +# Default-Stop: +# Short-Description: Daemon sys-polld +### END INIT INFO + +N=/etc/init.d/sys-polld +D_PATH=/opt/system-check/system-check.sh + +set -e + +stop_syspolld () { + local GCOUNT=`ps aux | grep -c "$D_PATH"` + local KILL_COUNT=0 + + while [ $GCOUNT -gt 1 ] + do + local ID=`ps aux | grep "$D_PATH" | head -n 1 | awk -F' ' '{print $2}'` + kill -15 $ID + GCOUNT=`ps aux | grep -c "$D_PATH"` + KILL_COUNT=`expr $KILL_COUNT + 1` + if [ $KILL_COUNT -eq 100 ]; then + echo "Error: kill sys-polld $KILL_COUNT times. stop kill process." + GCOUNT=0 + fi + done +} + +case "$1" in + start) + $D_PATH & + ;; + stop) + stop_syspolld + ;; + reload|restart|force-reload) + ;; + *) + echo "Usage: $N {start}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/sys_servd b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/sys_servd new file mode 100755 index 000000000000..09ee087d5db0 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/sys_servd @@ -0,0 +1,28 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: sys-servd +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: rc.local +# Default-Stop: 0 1 6 +# Short-Description: Daemon sys-servd +### END INIT INFO + +# Author: Eddy Weng + +DESC="Service script for /opt/sys-serv/sys-servd" +DAEMON=/opt/sys-serv/sys-servd + +case "$1" in + start) + start-stop-daemon --start --exec $DAEMON + ;; + stop) + start-stop-daemon --stop --name sys-servd + ;; + *) + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/xcvr_servd b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/xcvr_servd new file mode 100755 index 000000000000..1558614fd43c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/etc/init.d/xcvr_servd @@ -0,0 +1,28 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: xcvr-servd +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: rc.local +# Default-Stop: 0 1 6 +# Short-Description: Daemon xcvr-servd +### END INIT INFO + +# Author: Yencheng Lin + +DESC="Service script for /opt/xcvr-serv/xcvr-servd" +DAEMON=/opt/xcvr-serv/xcvr-servd + +case "$1" in + start) + start-stop-daemon --start --exec $DAEMON + ;; + stop) + start-stop-daemon --stop --name xcvr-servd + ;; + *) + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/Makefile b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/Makefile new file mode 100644 index 000000000000..fc3f88de8eb3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/Makefile @@ -0,0 +1,5 @@ +obj-m:=mitac_ly1200_32x_fb_i2c.o mitac_ly1200_32x_fse000.o \ +mitac_ly1200_32x_master_cpld.o mitac_ly1200_32x_sb_i2c.o \ +mitac_ly1200_32x_system_cpld.o mitac_ly1200_32x_cb_i2c.o \ +mitac_ly1200_32x_fb_module_i2c.o mitac_ly1200_32x_gpe.o \ +mitac_ly1200_32x_pb_i2c.o mitac_ly1200_32x_slave_cpld.o diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/bms_i2c.h b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/bms_i2c.h new file mode 100644 index 000000000000..dd63607f39c7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/bms_i2c.h @@ -0,0 +1,26 @@ +#ifndef _BMS_I2C_H_ +#define _BMS_I2C_H_ + +const char *bms_i2c_adapter_names[] = { + "SMBus I801 adapter", + "SMBus iSMT adapter", +}; + +enum i2c_adapter_type { + I2C_ADAPTER_I801 = 0, + I2C_ADAPTER_ISMT, +}; + +enum bms_module_switch_bus { + I2C_STAGE1_MUX_CHAN0 = 0, + I2C_STAGE1_MUX_CHAN1, + I2C_STAGE1_MUX_CHAN2, + I2C_STAGE1_MUX_CHAN3, + I2C_STAGE1_MUX_CHAN4, + I2C_STAGE1_MUX_CHAN5, + I2C_STAGE1_MUX_CHAN6, + I2C_STAGE1_MUX_CHAN7, +}; + +#endif /* _BMS_I2C_H_ */ + diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/master_cpld_reg.h b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/master_cpld_reg.h new file mode 100644 index 000000000000..dfdc89dbe57d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/master_cpld_reg.h @@ -0,0 +1,275 @@ +#ifndef __MASTER_CPLD_REG +#define __MASTER_CPLD_REG + +static int master_cpld_raw_read(struct device *dev, struct device_attribute *attr, char *buf, + int reg_offset, int reg_width, int fld_shift, int fld_width, int fld_mask, char* reg_name); +static int master_cpld_raw_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count, + int reg_offset, int reg_width, int fld_shift, int fld_width, int fld_mask, char* reg_name); + +/* generic CPLD read function */ +#define FLD_RAW_RD_FUNC(_reg, _fld, _wdh) static ssize_t \ +master_cpld_##_fld##_raw_read(struct device *dev, struct device_attribute *attr, char *buf) { \ + return master_cpld_raw_read(dev, attr, buf, _reg##_offset, _reg##_width, _fld##_shift, _fld##_width, _fld##_mask, #_reg); \ +} + +/* generic CPLD write function */ +#define FLD_RAW_WR_FUNC(_reg, _fld, _wdh) static ssize_t \ +master_cpld_##_fld##_raw_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { \ + return master_cpld_raw_write(dev, attr, buf, count, _reg##_offset, _reg##_width, _fld##_shift, _fld##_width, _fld##_mask, #_reg); \ +} + +/* CPLD register definition macros */ +#define REG_DEF(_reg, _off, _wdh) \ +static unsigned int _reg##_offset = (unsigned int)(_off); \ +static unsigned int _reg##_width = (unsigned int)(_wdh); + +/* CPLD register field definition macros, with generic read/write function */ +#define FLD_RAW_RO_DEF(_reg, _fld, _sft, _wdh) \ +static unsigned int _fld##_shift = (unsigned int)(_sft); \ +static unsigned int _fld##_width = (unsigned int)(_wdh); \ +static unsigned int _fld##_mask = ((((unsigned int)1) << (_wdh)) - 1); \ +FLD_RAW_RD_FUNC(_reg, _fld, _wdh) + +#define FLD_RAW_RW_DEF(_reg, _fld, _sft, _wdh) \ +static unsigned int _fld##_shift = (unsigned int)(_sft); \ +static unsigned int _fld##_width = (unsigned int)(_wdh); \ +static unsigned int _fld##_mask = ((((unsigned int)1) << (_wdh)) - 1); \ +FLD_RAW_RD_FUNC(_reg, _fld, _wdh) FLD_RAW_WR_FUNC(_reg, _fld, _wdh) + +/* declare master CPLD registers */ +/* register name offset width */ +/* --------------------------------------- ------- ----- */ +REG_DEF( mstr_cpld_rev, 0x00, 8) +REG_DEF( mstr_cpld_gpr, 0x01, 8) +REG_DEF( mb_brd_rev_type, 0x02, 8) +REG_DEF( mstr_srr, 0x03, 8) +REG_DEF( eeprom_wp, 0x04, 8) +REG_DEF( mstr_irq, 0x05, 8) +REG_DEF( system_led, 0x06, 8) +REG_DEF( fan_tray_3_1_led, 0x07, 8) +REG_DEF( fan_tray_6_4_led, 0x08, 8) +REG_DEF( fan_tray_status, 0x09, 8) +REG_DEF( fan_type_status, 0x0A, 8) +REG_DEF( psu_en_status, 0x0B, 8) +REG_DEF( mb_pwr_en_status, 0x0C, 8) +REG_DEF( mb_pwr_status, 0x0D, 8) + +REG_DEF( zqsfp28_present_8_1_status, 0x10, 8) +REG_DEF( zqsfp28_present_16_9_status, 0x11, 8) +REG_DEF( zqsfp28_rst_8_1, 0x12, 8) +REG_DEF( zqsfp28_rst_16_9, 0x13, 8) +REG_DEF( zqsfp28_modsel_8_1, 0x14, 8) +REG_DEF( zqsfp28_modsel_16_9, 0x15, 8) +REG_DEF( zqsfp28_lpmode_8_1, 0x16, 8) +REG_DEF( zqsfp28_lpmode_16_9, 0x17, 8) +REG_DEF( zqsfp28_irq_8_1_status, 0x18, 8) +REG_DEF( zqsfp28_irq_16_9_status, 0x19, 8) +REG_DEF( zqsfp28_irq_msk_8_1_status, 0x1A, 8) +REG_DEF( zqsfp28_irq_msk_16_9_status, 0x1B, 8) + + +/* declare master CPLD register's fields */ +/* register name field name shift width */ +/* ---------------------- ---------------- ------ ----- */ +FLD_RAW_RO_DEF( mstr_cpld_rev, mjr_rev, 4, 4) +FLD_RAW_RO_DEF( mstr_cpld_rev, mnr_rev, 0, 4) + +FLD_RAW_RW_DEF( mstr_cpld_gpr, scrtch_reg, 0, 8) + +FLD_RAW_RO_DEF( mb_brd_rev_type, brd_rev, 4, 4) +FLD_RAW_RO_DEF( mb_brd_rev_type, brd_type, 0, 4) + +FLD_RAW_RW_DEF( mstr_srr, mb_rst, 2, 1) +FLD_RAW_RW_DEF( mstr_srr, npu_rst, 1, 1) +FLD_RAW_RW_DEF( mstr_srr, mgmt_phy_rst, 0, 1) + +FLD_RAW_RW_DEF( eeprom_wp, mb_eeprom_wp, 2, 1) +FLD_RAW_RW_DEF( eeprom_wp, cpld_spi_wp, 1, 1) +FLD_RAW_RW_DEF( eeprom_wp, fan_eeprom_wp, 0, 1) + +FLD_RAW_RW_DEF( mstr_irq, ps2_int_msk, 7, 1) +FLD_RAW_RW_DEF( mstr_irq, ps1_int_msk, 6, 1) +FLD_RAW_RW_DEF( mstr_irq, usb_fault_msk, 5, 1) +FLD_RAW_RW_DEF( mstr_irq, pcie_int_msk, 4, 1) +FLD_RAW_RW_DEF( mstr_irq, fan_alert_int_msk, 3, 1) +FLD_RAW_RO_DEF( mstr_irq, usb_fault, 2, 1) +FLD_RAW_RO_DEF( mstr_irq, pcie_int, 1, 1) +FLD_RAW_RO_DEF( mstr_irq, fan_alert_int, 0, 1) + +FLD_RAW_RW_DEF( system_led, system_led_fld, 6, 2) +FLD_RAW_RW_DEF( system_led, power_led, 4, 2) +FLD_RAW_RW_DEF( system_led, fan_led, 2, 2) +FLD_RAW_RW_DEF( system_led, locate_led, 1, 1) + +FLD_RAW_RW_DEF( fan_tray_3_1_led, led_test, 6, 2) +FLD_RAW_RW_DEF( fan_tray_3_1_led, fan_tray3_led, 4, 2) +FLD_RAW_RW_DEF( fan_tray_3_1_led, fan_tray2_led, 2, 2) +FLD_RAW_RW_DEF( fan_tray_3_1_led, fan_tray1_led, 0, 2) + +FLD_RAW_RW_DEF( fan_tray_6_4_led, fan_tray6_led, 4, 2) +FLD_RAW_RW_DEF( fan_tray_6_4_led, fan_tray5_led, 2, 2) +FLD_RAW_RW_DEF( fan_tray_6_4_led, fan_tray4_led, 0, 2) + +FLD_RAW_RO_DEF( fan_tray_status, fan_tray6_present, 5, 1) +FLD_RAW_RO_DEF( fan_tray_status, fan_tray5_present, 4, 1) +FLD_RAW_RO_DEF( fan_tray_status, fan_tray4_present, 3, 1) +FLD_RAW_RO_DEF( fan_tray_status, fan_tray3_present, 2, 1) +FLD_RAW_RO_DEF( fan_tray_status, fan_tray2_present, 1, 1) +FLD_RAW_RO_DEF( fan_tray_status, fan_tray1_present, 0, 1) + +FLD_RAW_RO_DEF( fan_type_status, fan_type6, 5, 1) +FLD_RAW_RO_DEF( fan_type_status, fan_type5, 4, 1) +FLD_RAW_RO_DEF( fan_type_status, fan_type4, 3, 1) +FLD_RAW_RO_DEF( fan_type_status, fan_type3, 2, 1) +FLD_RAW_RO_DEF( fan_type_status, fan_type2, 1, 1) +FLD_RAW_RO_DEF( fan_type_status, fan_type1, 0, 1) + +FLD_RAW_RO_DEF( psu_en_status, ps1_ps, 7, 1) +FLD_RAW_RO_DEF( psu_en_status, ps1_pg, 6, 1) +FLD_RAW_RO_DEF( psu_en_status, ps1_int, 5, 1) +FLD_RAW_RW_DEF( psu_en_status, ps1_on, 4, 1) +FLD_RAW_RO_DEF( psu_en_status, ps2_ps, 3, 1) +FLD_RAW_RO_DEF( psu_en_status, ps2_pg, 2, 1) +FLD_RAW_RO_DEF( psu_en_status, ps2_int, 1, 1) +FLD_RAW_RW_DEF( psu_en_status, ps2_on, 0, 1) + +FLD_RAW_RW_DEF( mb_pwr_en_status, usb1_vbus_en, 7, 1) +FLD_RAW_RO_DEF( mb_pwr_en_status, v5p0_en, 5, 1) +FLD_RAW_RO_DEF( mb_pwr_en_status, v3p3_en, 4, 1) +FLD_RAW_RO_DEF( mb_pwr_en_status, vcc_1v8_en, 3, 1) +FLD_RAW_RO_DEF( mb_pwr_en_status, mac_avs1v_en, 2, 1) +FLD_RAW_RO_DEF( mb_pwr_en_status, mac1v_en, 1, 1) +FLD_RAW_RO_DEF( mb_pwr_en_status, vcc_1v25_en, 0, 1) + +FLD_RAW_RO_DEF( mb_pwr_status, vcc_3p3_cpld, 6, 1) +FLD_RAW_RO_DEF( mb_pwr_status, vcc5v_pg, 5, 1) +FLD_RAW_RO_DEF( mb_pwr_status, vcc3v3_pg, 4, 1) +FLD_RAW_RO_DEF( mb_pwr_status, vcc_1v8_pg, 3, 1) +FLD_RAW_RO_DEF( mb_pwr_status, mac_avs1v_pg, 2, 1) +FLD_RAW_RO_DEF( mb_pwr_status, mac1v_pg, 1, 1) +FLD_RAW_RO_DEF( mb_pwr_status, vcc_1v25_pg, 0, 1) + +FLD_RAW_RO_DEF( zqsfp28_present_8_1_status, port8_present, 7, 1) +FLD_RAW_RO_DEF( zqsfp28_present_8_1_status, port7_present, 6, 1) +FLD_RAW_RO_DEF( zqsfp28_present_8_1_status, port6_present, 5, 1) +FLD_RAW_RO_DEF( zqsfp28_present_8_1_status, port5_present, 4, 1) +FLD_RAW_RO_DEF( zqsfp28_present_8_1_status, port4_present, 3, 1) +FLD_RAW_RO_DEF( zqsfp28_present_8_1_status, port3_present, 2, 1) +FLD_RAW_RO_DEF( zqsfp28_present_8_1_status, port2_present, 1, 1) +FLD_RAW_RO_DEF( zqsfp28_present_8_1_status, port1_present, 0, 1) + +FLD_RAW_RO_DEF( zqsfp28_present_16_9_status,port16_present, 7, 1) +FLD_RAW_RO_DEF( zqsfp28_present_16_9_status,port15_present, 6, 1) +FLD_RAW_RO_DEF( zqsfp28_present_16_9_status,port14_present, 5, 1) +FLD_RAW_RO_DEF( zqsfp28_present_16_9_status,port13_present, 4, 1) +FLD_RAW_RO_DEF( zqsfp28_present_16_9_status,port12_present, 3, 1) +FLD_RAW_RO_DEF( zqsfp28_present_16_9_status,port11_present, 2, 1) +FLD_RAW_RO_DEF( zqsfp28_present_16_9_status,port10_present, 1, 1) +FLD_RAW_RO_DEF( zqsfp28_present_16_9_status,port9_present, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_rst_8_1, port8_rst, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_8_1, port7_rst, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_8_1, port6_rst, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_8_1, port5_rst, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_8_1, port4_rst, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_8_1, port3_rst, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_8_1, port2_rst, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_8_1, port1_rst, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_rst_16_9, port16_rst, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_16_9, port15_rst, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_16_9, port14_rst, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_16_9, port13_rst, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_16_9, port12_rst, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_16_9, port11_rst, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_16_9, port10_rst, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_16_9, port9_rst, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_modsel_8_1, port8_modsel, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_8_1, port7_modsel, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_8_1, port6_modsel, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_8_1, port5_modsel, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_8_1, port4_modsel, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_8_1, port3_modsel, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_8_1, port2_modsel, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_8_1, port1_modsel, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_modsel_16_9, port16_modsel, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_16_9, port15_modsel, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_16_9, port14_modsel, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_16_9, port13_modsel, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_16_9, port12_modsel, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_16_9, port11_modsel, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_16_9, port10_modsel, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_16_9, port9_modsel, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_lpmode_8_1, port8_lpmode, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_8_1, port7_lpmode, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_8_1, port6_lpmode, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_8_1, port5_lpmode, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_8_1, port4_lpmode, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_8_1, port3_lpmode, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_8_1, port2_lpmode, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_8_1, port1_lpmode, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_lpmode_16_9, port16_lpmode, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_16_9, port15_lpmode, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_16_9, port14_lpmode, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_16_9, port13_lpmode, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_16_9, port12_lpmode, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_16_9, port11_lpmode, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_16_9, port10_lpmode, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_16_9, port9_lpmode, 0, 1) + +FLD_RAW_RO_DEF( zqsfp28_irq_8_1_status, port8_irq_status, 7, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_8_1_status, port7_irq_status, 6, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_8_1_status, port6_irq_status, 5, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_8_1_status, port5_irq_status, 4, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_8_1_status, port4_irq_status, 3, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_8_1_status, port3_irq_status, 2, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_8_1_status, port2_irq_status, 1, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_8_1_status, port1_irq_status, 0, 1) + +FLD_RAW_RO_DEF( zqsfp28_irq_16_9_status, port16_irq_status, 7, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_16_9_status, port15_irq_status, 6, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_16_9_status, port14_irq_status, 5, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_16_9_status, port13_irq_status, 4, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_16_9_status, port12_irq_status, 3, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_16_9_status, port11_irq_status, 2, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_16_9_status, port10_irq_status, 1, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_16_9_status, port9_irq_status, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_irq_msk_8_1_status, port8_irq_msk, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_8_1_status, port7_irq_msk, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_8_1_status, port6_irq_msk, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_8_1_status, port5_irq_msk, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_8_1_status, port4_irq_msk, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_8_1_status, port3_irq_msk, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_8_1_status, port2_irq_msk, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_8_1_status, port1_irq_msk, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_irq_msk_16_9_status,port16_irq_msk, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_16_9_status,port15_irq_msk, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_16_9_status,port14_irq_msk, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_16_9_status,port13_irq_msk, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_16_9_status,port12_irq_msk, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_16_9_status,port11_irq_msk, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_16_9_status,port10_irq_msk, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_16_9_status,port9_irq_msk, 0, 1) + +FLD_RAW_RO_DEF( zqsfp28_present_8_1_status, port_1_8_present, 0, 8) +FLD_RAW_RO_DEF( zqsfp28_present_16_9_status,port_9_16_present, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_rst_8_1, port_1_8_rst, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_rst_16_9, port_9_16_rst, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_modsel_8_1, port_1_8_modsel, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_modsel_16_9, port_9_16_modsel, 0, 8) +FLD_RAW_RO_DEF( zqsfp28_irq_8_1_status, port_1_8_irq_status, 0, 8) +FLD_RAW_RO_DEF( zqsfp28_irq_16_9_status, port_9_16_irq_status,0, 8) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_8_1_status, port_1_8_irq_msk, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_16_9_status,port_9_16_irq_msk, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_lpmode_8_1, port_1_8_lpmode, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_lpmode_16_9, port_9_16_lpmode, 0, 8) + +FLD_RAW_RO_DEF( fan_tray_status, fan_tray1_6_present, 0, 8) +FLD_RAW_RO_DEF( psu_en_status, psu_en_status_fld, 0, 8) +#endif /* __MASTER_CPLD_REG */ diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/master_cpld_sysfs.h b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/master_cpld_sysfs.h new file mode 100644 index 000000000000..e851ca5fb487 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/master_cpld_sysfs.h @@ -0,0 +1,226 @@ +#ifndef __MASTER_CPLD_SYSFS +#define __MASTER_CPLD_SYSFS + +/* generic CPLD sysfs file definition macros */ +#define SYSFS_RAW_RO_ATTR_DEF(field) \ +struct device_attribute field \ + = __ATTR(field, S_IRUGO, master_cpld_##field##_raw_read, NULL); + +#define SYSFS_RAW_RW_ATTR_DEF(field) \ +struct device_attribute field \ + = __ATTR(field, S_IRUGO | S_IWUGO, master_cpld_##field##_raw_read, master_cpld_##field##_raw_write); + +#define SYSFS_MISC_RO_ATTR_DEF(field, _read) \ +struct device_attribute field \ + = __ATTR(field, S_IRUGO, _read, NULL); + +#define SYSFS_MISC_RW_ATTR_DEF(field, _read, _write) \ +struct device_attribute field \ + = __ATTR(field, S_IRUGO | S_IWUGO, _read, _write); + +#define SYSFS_ATTR_PTR(field) \ +&field.attr + +/* declare master CPLD file system */ +SYSFS_RAW_RO_ATTR_DEF(mjr_rev) +SYSFS_RAW_RO_ATTR_DEF(mnr_rev) + +SYSFS_RAW_RW_ATTR_DEF(scrtch_reg) + +SYSFS_RAW_RO_ATTR_DEF(brd_rev) +SYSFS_RAW_RO_ATTR_DEF(brd_type) + +SYSFS_RAW_RW_ATTR_DEF(mb_rst) +SYSFS_RAW_RW_ATTR_DEF(npu_rst) +SYSFS_RAW_RW_ATTR_DEF(mgmt_phy_rst) + +SYSFS_RAW_RW_ATTR_DEF(mb_eeprom_wp) +SYSFS_RAW_RW_ATTR_DEF(cpld_spi_wp) +SYSFS_RAW_RW_ATTR_DEF(fan_eeprom_wp) + +SYSFS_RAW_RW_ATTR_DEF(ps2_int_msk) +SYSFS_RAW_RW_ATTR_DEF(ps1_int_msk) +SYSFS_RAW_RW_ATTR_DEF(usb_fault_msk) +SYSFS_RAW_RW_ATTR_DEF(pcie_int_msk) +SYSFS_RAW_RW_ATTR_DEF(fan_alert_int_msk) +SYSFS_RAW_RO_ATTR_DEF(usb_fault) +SYSFS_RAW_RO_ATTR_DEF(pcie_int) +SYSFS_RAW_RO_ATTR_DEF(fan_alert_int) + +SYSFS_RAW_RW_ATTR_DEF(system_led_fld) +SYSFS_RAW_RW_ATTR_DEF(power_led) +SYSFS_RAW_RW_ATTR_DEF(fan_led) +SYSFS_RAW_RW_ATTR_DEF(locate_led) + +SYSFS_RAW_RW_ATTR_DEF(led_test) +SYSFS_RAW_RW_ATTR_DEF(fan_tray3_led) +SYSFS_RAW_RW_ATTR_DEF(fan_tray2_led) +SYSFS_RAW_RW_ATTR_DEF(fan_tray1_led) + +SYSFS_RAW_RW_ATTR_DEF(fan_tray6_led) +SYSFS_RAW_RW_ATTR_DEF(fan_tray5_led) +SYSFS_RAW_RW_ATTR_DEF(fan_tray4_led) + +SYSFS_RAW_RO_ATTR_DEF(fan_tray6_present) +SYSFS_RAW_RO_ATTR_DEF(fan_tray5_present) +SYSFS_RAW_RO_ATTR_DEF(fan_tray4_present) +SYSFS_RAW_RO_ATTR_DEF(fan_tray3_present) +SYSFS_RAW_RO_ATTR_DEF(fan_tray2_present) +SYSFS_RAW_RO_ATTR_DEF(fan_tray1_present) + +SYSFS_RAW_RO_ATTR_DEF(fan_type6) +SYSFS_RAW_RO_ATTR_DEF(fan_type5) +SYSFS_RAW_RO_ATTR_DEF(fan_type4) +SYSFS_RAW_RO_ATTR_DEF(fan_type3) +SYSFS_RAW_RO_ATTR_DEF(fan_type2) +SYSFS_RAW_RO_ATTR_DEF(fan_type1) + +SYSFS_RAW_RO_ATTR_DEF(ps1_ps) +SYSFS_RAW_RO_ATTR_DEF(ps1_pg) +SYSFS_RAW_RO_ATTR_DEF(ps1_int) +SYSFS_RAW_RW_ATTR_DEF(ps1_on) +SYSFS_RAW_RO_ATTR_DEF(ps2_ps) +SYSFS_RAW_RO_ATTR_DEF(ps2_pg) +SYSFS_RAW_RO_ATTR_DEF(ps2_int) +SYSFS_RAW_RW_ATTR_DEF(ps2_on) + +SYSFS_RAW_RW_ATTR_DEF(usb1_vbus_en) +SYSFS_RAW_RO_ATTR_DEF(v5p0_en) +SYSFS_RAW_RO_ATTR_DEF(v3p3_en) +SYSFS_RAW_RO_ATTR_DEF(vcc_1v8_en) +SYSFS_RAW_RO_ATTR_DEF(mac_avs1v_en) +SYSFS_RAW_RO_ATTR_DEF(mac1v_en) +SYSFS_RAW_RO_ATTR_DEF(vcc_1v25_en) + +SYSFS_RAW_RO_ATTR_DEF(vcc_3p3_cpld) +SYSFS_RAW_RO_ATTR_DEF(vcc5v_pg) +SYSFS_RAW_RO_ATTR_DEF(vcc3v3_pg) +SYSFS_RAW_RO_ATTR_DEF(vcc_1v8_pg) +SYSFS_RAW_RO_ATTR_DEF(mac_avs1v_pg) +SYSFS_RAW_RO_ATTR_DEF(mac1v_pg) +SYSFS_RAW_RO_ATTR_DEF(vcc_1v25_pg) + +SYSFS_RAW_RO_ATTR_DEF(port8_present) +SYSFS_RAW_RO_ATTR_DEF(port7_present) +SYSFS_RAW_RO_ATTR_DEF(port6_present) +SYSFS_RAW_RO_ATTR_DEF(port5_present) +SYSFS_RAW_RO_ATTR_DEF(port4_present) +SYSFS_RAW_RO_ATTR_DEF(port3_present) +SYSFS_RAW_RO_ATTR_DEF(port2_present) +SYSFS_RAW_RO_ATTR_DEF(port1_present) + +SYSFS_RAW_RO_ATTR_DEF(port16_present) +SYSFS_RAW_RO_ATTR_DEF(port15_present) +SYSFS_RAW_RO_ATTR_DEF(port14_present) +SYSFS_RAW_RO_ATTR_DEF(port13_present) +SYSFS_RAW_RO_ATTR_DEF(port12_present) +SYSFS_RAW_RO_ATTR_DEF(port11_present) +SYSFS_RAW_RO_ATTR_DEF(port10_present) +SYSFS_RAW_RO_ATTR_DEF(port9_present) + +SYSFS_RAW_RW_ATTR_DEF(port8_rst) +SYSFS_RAW_RW_ATTR_DEF(port7_rst) +SYSFS_RAW_RW_ATTR_DEF(port6_rst) +SYSFS_RAW_RW_ATTR_DEF(port5_rst) +SYSFS_RAW_RW_ATTR_DEF(port4_rst) +SYSFS_RAW_RW_ATTR_DEF(port3_rst) +SYSFS_RAW_RW_ATTR_DEF(port2_rst) +SYSFS_RAW_RW_ATTR_DEF(port1_rst) + +SYSFS_RAW_RW_ATTR_DEF(port16_rst) +SYSFS_RAW_RW_ATTR_DEF(port15_rst) +SYSFS_RAW_RW_ATTR_DEF(port14_rst) +SYSFS_RAW_RW_ATTR_DEF(port13_rst) +SYSFS_RAW_RW_ATTR_DEF(port12_rst) +SYSFS_RAW_RW_ATTR_DEF(port11_rst) +SYSFS_RAW_RW_ATTR_DEF(port10_rst) +SYSFS_RAW_RW_ATTR_DEF(port9_rst) + +SYSFS_RAW_RW_ATTR_DEF(port8_modsel) +SYSFS_RAW_RW_ATTR_DEF(port7_modsel) +SYSFS_RAW_RW_ATTR_DEF(port6_modsel) +SYSFS_RAW_RW_ATTR_DEF(port5_modsel) +SYSFS_RAW_RW_ATTR_DEF(port4_modsel) +SYSFS_RAW_RW_ATTR_DEF(port3_modsel) +SYSFS_RAW_RW_ATTR_DEF(port2_modsel) +SYSFS_RAW_RW_ATTR_DEF(port1_modsel) + +SYSFS_RAW_RW_ATTR_DEF(port16_modsel) +SYSFS_RAW_RW_ATTR_DEF(port15_modsel) +SYSFS_RAW_RW_ATTR_DEF(port14_modsel) +SYSFS_RAW_RW_ATTR_DEF(port13_modsel) +SYSFS_RAW_RW_ATTR_DEF(port12_modsel) +SYSFS_RAW_RW_ATTR_DEF(port11_modsel) +SYSFS_RAW_RW_ATTR_DEF(port10_modsel) +SYSFS_RAW_RW_ATTR_DEF(port9_modsel) + +SYSFS_RAW_RW_ATTR_DEF(port8_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port7_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port6_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port5_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port4_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port3_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port2_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port1_lpmode) + +SYSFS_RAW_RW_ATTR_DEF(port16_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port15_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port14_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port13_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port12_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port11_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port10_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port9_lpmode) + +SYSFS_RAW_RO_ATTR_DEF(port8_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port7_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port6_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port5_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port4_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port3_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port2_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port1_irq_status) + +SYSFS_RAW_RO_ATTR_DEF(port16_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port15_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port14_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port13_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port12_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port11_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port10_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port9_irq_status) + +SYSFS_RAW_RW_ATTR_DEF(port8_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port7_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port6_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port5_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port4_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port3_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port2_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port1_irq_msk) + +SYSFS_RAW_RW_ATTR_DEF(port16_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port15_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port14_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port13_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port12_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port11_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port10_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port9_irq_msk) + +SYSFS_RAW_RO_ATTR_DEF(port_1_8_present) +SYSFS_RAW_RO_ATTR_DEF(port_9_16_present) +SYSFS_RAW_RW_ATTR_DEF(port_1_8_rst) +SYSFS_RAW_RW_ATTR_DEF(port_9_16_rst) +SYSFS_RAW_RW_ATTR_DEF(port_1_8_modsel) +SYSFS_RAW_RW_ATTR_DEF(port_9_16_modsel) +SYSFS_RAW_RO_ATTR_DEF(port_1_8_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port_9_16_irq_status) +SYSFS_RAW_RW_ATTR_DEF(port_1_8_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port_9_16_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port_1_8_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port_9_16_lpmode) + +SYSFS_RAW_RO_ATTR_DEF(fan_tray1_6_present) +SYSFS_RAW_RO_ATTR_DEF(psu_en_status_fld) +#endif /* __MASTER_CPLD_SYSFS */ diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_cb_i2c.c b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_cb_i2c.c new file mode 100644 index 000000000000..11fffe251785 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_cb_i2c.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include "bms_i2c.h" + +#define BMS_CB_I2C_CLIENT_NUM 5 +#define BMS_CB_ADAPTER_BASE 0 + +static struct i2c_client *bms_cb_clients[BMS_CB_I2C_CLIENT_NUM] = {NULL}; +static int bms_cb_client_index = 0; + +static int __init __find_i2c_adap(struct device *dev, void *data) +{ + const char *name = data; + static const char *prefix = "i2c-"; + struct i2c_adapter *adapter; + + if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0) + { + return 0; + } + adapter = to_i2c_adapter(dev); + + return (strncmp(adapter->name, name, strlen(name)) == 0); +} + +static int __init find_i2c_adapter_num(enum i2c_adapter_type type) +{ + struct device *dev = NULL; + struct i2c_adapter *adapter; + const char *name = bms_i2c_adapter_names[type]; + + /* find the adapter by name */ + dev = bus_find_device(&i2c_bus_type, NULL, (void *)name, + __find_i2c_adap); + if (!dev) { + pr_err("%s: i2c adapter %s not found on system.\n", + __func__, name); + return -ENODEV; + } + adapter = to_i2c_adapter(dev); + + return adapter->nr; +} + + +static __init struct i2c_client *bms_cb_setup_spd( + struct i2c_adapter *adap, int addr) +{ + struct i2c_board_info info_spd = { + I2C_BOARD_INFO("spd", addr), + }; + + return i2c_new_device(adap, &info_spd); +} + + +static __init struct i2c_client *bms_cb_setup_eeprom_24c02( + struct i2c_adapter *adap, int addr) +{ + struct i2c_board_info info_spd = { + I2C_BOARD_INFO("24c02", addr), + }; + + return i2c_new_device(adap, &info_spd); +} + +static __init struct i2c_client *bms_cb_setup_tmp75( + struct i2c_adapter *adap, int addr) +{ + struct i2c_board_info info_spd = { + I2C_BOARD_INFO("tmp75", addr), + }; + + return i2c_new_device(adap, &info_spd); +} + +static __init struct i2c_client *bms_cb_setup_system_cpld(struct i2c_adapter *adap) +{ + struct i2c_board_info info = { + I2C_BOARD_INFO("system_cpld", 0x31), + }; + + return i2c_new_device(adap, &info); +} + +static int __init bms_cb_setup_devices_i801(void) +{ + struct i2c_adapter *adap; + int adap_num = find_i2c_adapter_num(I2C_ADAPTER_I801); + + if (adap_num < 0) + return adap_num; + + adap = i2c_get_adapter(adap_num); + if (!adap) { + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + goto exit; + } + + bms_cb_clients[bms_cb_client_index++] = bms_cb_setup_spd(adap, 0x50); + bms_cb_clients[bms_cb_client_index++] = bms_cb_setup_spd(adap, 0x52); + bms_cb_clients[bms_cb_client_index++] = bms_cb_setup_tmp75(adap, 0x4e); + +exit: + return 0; +} + +static int __init bms_cb_setup_devices_ismt(void) +{ + struct i2c_adapter *adap; + int adap_num = find_i2c_adapter_num(I2C_ADAPTER_ISMT); + + if (adap_num < 0) + return adap_num; + + adap = i2c_get_adapter(adap_num); + if (!adap) { + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + return 0; + } + + bms_cb_clients[bms_cb_client_index++] = bms_cb_setup_system_cpld(adap); + bms_cb_clients[bms_cb_client_index++] = bms_cb_setup_eeprom_24c02(adap, 0x56); + return 0; +} + +static int __init bms_cb_i2c_init(void) +{ + /* Initial bms_cb_slients array. */ + memset(bms_cb_clients, 0x0, BMS_CB_I2C_CLIENT_NUM); + + bms_cb_setup_devices_i801(); + mdelay(200); + bms_cb_setup_devices_ismt(); + + return 0; +} + + +static void __exit bms_cb_i2c_exit(void){ + int i; + + for (i=(bms_cb_client_index-1); i>=0; i--) { + if (bms_cb_clients[i]) { + i2c_unregister_device(bms_cb_clients[i]); + bms_cb_clients[i] = NULL; + } + } + + bms_cb_client_index = 0; + +} + + +module_init(bms_cb_i2c_init); +module_exit(bms_cb_i2c_exit); + + +MODULE_DESCRIPTION("mitac_ly1200_32x_cb_i2c driver"); +MODULE_AUTHOR("Eddy Weng "); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_fb_i2c.c b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_fb_i2c.c new file mode 100644 index 000000000000..d228bc481279 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_fb_i2c.c @@ -0,0 +1,153 @@ +#include +#include +#include "bms_i2c.h" + +#define BMS_FB_I2C_CLIENT_NUM 3 + + +static struct i2c_client *bms_fb_clients[BMS_FB_I2C_CLIENT_NUM] = {NULL}; +static int bms_fb_client_index = 0; + +static int __init __find_i2c_adap(struct device *dev, void *data) +{ + const char *name = data; + static const char *prefix = "i2c-"; + struct i2c_adapter *adapter; + + if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0) + { + return 0; + } + adapter = to_i2c_adapter(dev); + + return (strncmp(adapter->name, name, strlen(name)) == 0); +} + +static int __init find_i2c_adapter_num(enum i2c_adapter_type type) +{ + struct device *dev = NULL; + struct i2c_adapter *adapter; + const char *name = bms_i2c_adapter_names[type]; + + /* find the adapter by name */ + dev = bus_find_device(&i2c_bus_type, NULL, (void *)name, + __find_i2c_adap); + if (!dev) { + pr_err("%s: i2c adapter %s not found on system.\n", + __func__, name); + return -ENODEV; + } + adapter = to_i2c_adapter(dev); + + return adapter->nr; +} + +static int __init find_i2c_mux_adapter_num(int parent_num, int num) +{ + struct device *dev = NULL; + struct i2c_adapter *adapter; + char name[48]; + + snprintf(name, sizeof(name), "i2c-%d-mux (chan_id %d)", + parent_num, num); + /* find the adapter by name */ + dev = bus_find_device(&i2c_bus_type, NULL, (void *)name, + __find_i2c_adap); + if (!dev) { + pr_err("%s: i2c adapter %s not found on system.\n", + __func__, name); + return -ENODEV; + } + adapter = to_i2c_adapter(dev); + + return adapter->nr; +} + +static __init struct i2c_client *bms_fb_setup_tmp75( + struct i2c_adapter *adap, int addr) +{ + struct i2c_board_info info_spd = { + I2C_BOARD_INFO("tmp75", addr), + }; + + return i2c_new_device(adap, &info_spd); +} + +static __init struct i2c_client *bms_fb_setup_max31790( + struct i2c_adapter *adap, int addr) +{ + struct i2c_board_info info_spd = { + I2C_BOARD_INFO("max31790", addr), + }; + + return i2c_new_device(adap, &info_spd); +} + +static int __init bms_fb_setup_devices(void) +{ + struct i2c_adapter *adap; + int adap_num; + int parent_num; + + parent_num = find_i2c_adapter_num(I2C_ADAPTER_ISMT); + if (parent_num < 0) + return parent_num; + + /* Mux chan1 steup */ + adap_num = find_i2c_mux_adapter_num(parent_num, I2C_STAGE1_MUX_CHAN1); + if (adap_num >= 0){ + adap = i2c_get_adapter(adap_num); + if(adap) { + bms_fb_clients[bms_fb_client_index++] = bms_fb_setup_max31790(adap, 0x20); + bms_fb_clients[bms_fb_client_index++] = bms_fb_setup_max31790(adap, 0x23); + }else{ + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + } + }else{ + pr_err("%s failed to find i2c mux adap number %d.\n", __func__, I2C_STAGE1_MUX_CHAN1); + } + + adap_num = find_i2c_mux_adapter_num(parent_num, I2C_STAGE1_MUX_CHAN2); + if (adap_num >= 0){ + adap = i2c_get_adapter(adap_num); + if(adap) { + bms_fb_clients[bms_fb_client_index++] = bms_fb_setup_tmp75(adap, 0x4d); + }else{ + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + } + }else{ + pr_err("%s failed to find i2c mux adap number %d.\n", __func__, I2C_STAGE1_MUX_CHAN2); + } + + return 0; +} + +static int __init bms_fb_i2c_init(void) +{ + bms_fb_setup_devices(); + + return 0; +} + +static void __exit bms_fb_i2c_exit(void){ + int i; + + for (i=(bms_fb_client_index-1); i>=0; i--) { + if (bms_fb_clients[i]) { + i2c_unregister_device(bms_fb_clients[i]); + bms_fb_clients[i] = NULL; + } + } + + bms_fb_client_index = 0; + +} + +module_init(bms_fb_i2c_init); +module_exit(bms_fb_i2c_exit); + + +MODULE_DESCRIPTION("mitac_ly1200_32x_fb_i2c driver"); +MODULE_AUTHOR("Eddy Weng "); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_fb_module_i2c.c b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_fb_module_i2c.c new file mode 100644 index 000000000000..174dba197217 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_fb_module_i2c.c @@ -0,0 +1,138 @@ +#include +#include +#include "bms_i2c.h" + +#define BMS_FB_MODULE_I2C_CLIENT_NUM 6 + +static struct i2c_client *bms_fb_module_clients[BMS_FB_MODULE_I2C_CLIENT_NUM] = {NULL}; +static int bms_fb_module_client_index = 0; + +static int __init __find_i2c_adap(struct device *dev, void *data) +{ + const char *name = data; + static const char *prefix = "i2c-"; + struct i2c_adapter *adapter; + + if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0) + { + return 0; + } + adapter = to_i2c_adapter(dev); + + return (strncmp(adapter->name, name, strlen(name)) == 0); +} + +static int __init find_i2c_adapter_num(enum i2c_adapter_type type) +{ + struct device *dev = NULL; + struct i2c_adapter *adapter; + const char *name = bms_i2c_adapter_names[type]; + + /* find the adapter by name */ + dev = bus_find_device(&i2c_bus_type, NULL, (void *)name, + __find_i2c_adap); + if (!dev) { + pr_err("%s: i2c adapter %s not found on system.\n", + __func__, name); + return -ENODEV; + } + adapter = to_i2c_adapter(dev); + + return adapter->nr; +} + +static int __init find_i2c_mux_adapter_num(int parent_num, int num) +{ + struct device *dev = NULL; + struct i2c_adapter *adapter; + char name[48]; + + snprintf(name, sizeof(name), "i2c-%d-mux (chan_id %d)", + parent_num, num); + /* find the adapter by name */ + dev = bus_find_device(&i2c_bus_type, NULL, (void *)name, + __find_i2c_adap); + if (!dev) { + pr_err("%s: i2c adapter %s not found on system.\n", + __func__, name); + return -ENODEV; + } + adapter = to_i2c_adapter(dev); + + return adapter->nr; +} + + +static __init struct i2c_client *bms_fb_module_setup_eeprom_24c02( + struct i2c_adapter *adap, int addr) +{ + struct i2c_board_info info_spd = { + I2C_BOARD_INFO("24c02", addr), + }; + + return i2c_new_device(adap, &info_spd); +} + +static int __init bms_fb_module_setup_devices(void) +{ + struct i2c_adapter *adap; + int adap_num; + int parent_num; + + parent_num = find_i2c_adapter_num(I2C_ADAPTER_ISMT); + if (parent_num < 0) + return parent_num; + + adap_num = find_i2c_mux_adapter_num(parent_num, I2C_STAGE1_MUX_CHAN1); + if (adap_num < 0) + return adap_num; + + adap = i2c_get_adapter(adap_num); + if (!adap) { + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + goto exit; + } + + bms_fb_module_clients[bms_fb_module_client_index++] = bms_fb_module_setup_eeprom_24c02(adap, 0x50); + bms_fb_module_clients[bms_fb_module_client_index++] = bms_fb_module_setup_eeprom_24c02(adap, 0x51); + bms_fb_module_clients[bms_fb_module_client_index++] = bms_fb_module_setup_eeprom_24c02(adap, 0x52); + bms_fb_module_clients[bms_fb_module_client_index++] = bms_fb_module_setup_eeprom_24c02(adap, 0x53); + bms_fb_module_clients[bms_fb_module_client_index++] = bms_fb_module_setup_eeprom_24c02(adap, 0x54); + bms_fb_module_clients[bms_fb_module_client_index++] = bms_fb_module_setup_eeprom_24c02(adap, 0x55); + +exit: + return 0; +} + +static int __init bms_fb_module_i2c_init(void) +{ + /* Initial bms_sb_slients array. */ + memset(bms_fb_module_clients, 0x0, BMS_FB_MODULE_I2C_CLIENT_NUM); + + bms_fb_module_setup_devices(); + + return 0; +} + +static void __exit bms_fb_module_i2c_exit(void){ + int i; + + for (i=(bms_fb_module_client_index-1); i>=0; i--) { + if (bms_fb_module_clients[i]) { + i2c_unregister_device(bms_fb_module_clients[i]); + bms_fb_module_clients[i] = NULL; + } + } + + bms_fb_module_client_index = 0; + +} + +module_init(bms_fb_module_i2c_init); +module_exit(bms_fb_module_i2c_exit); + + +MODULE_DESCRIPTION("mitac_ly1200_32x_fb_module_i2c driver"); +MODULE_AUTHOR("Eddy Weng "); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_fse000.c b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_fse000.c new file mode 100644 index 000000000000..362821706857 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_fse000.c @@ -0,0 +1,1787 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmbus.h" +#include + +/* + * Number of additional attribute pointers to allocate + * with each call to krealloc + */ +#define PMBUS_ATTR_ALLOC_SIZE 32 + +/* + * Index into status register array, per status register group + */ +#define PB_STATUS_BASE 0 +#define PB_STATUS_VOUT_BASE (PB_STATUS_BASE + PMBUS_PAGES) +#define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES) +#define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES) +#define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES) +#define PB_STATUS_TEMP_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES) +#define PB_STATUS_INPUT_BASE (PB_STATUS_TEMP_BASE + PMBUS_PAGES) +#define PB_STATUS_VMON_BASE (PB_STATUS_INPUT_BASE + 1) + +#define PB_NUM_STATUS_REG (PB_STATUS_VMON_BASE + 1) + +#define PMBUS_NAME_SIZE 24 +#define PMBUS_BLOCK_READ_SIZE 32 + +struct pmbus_sensor { + struct pmbus_sensor *next; + char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ + struct device_attribute attribute; + u8 page; /* page number */ + u16 reg; /* register */ + enum pmbus_sensor_classes class; /* sensor class */ + bool update; /* runtime sensor update needed */ + int data; /* Sensor data. + Negative if there was a read error */ +}; +#define to_pmbus_sensor(_attr) \ + container_of(_attr, struct pmbus_sensor, attribute) + +struct pmbus_st { + char name[PMBUS_NAME_SIZE]; /* sysfs boolean name */ + struct sensor_device_attribute attribute; + u16 base; +}; +#define to_pmbus_status(_attr) \ + container_of(_attr, struct pmbus_st, attribute) + +struct pmbus_mfr { + struct pmbus_mfr *next; + char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ + struct device_attribute attribute; + u8 page; /* page number */ + u16 reg; /* register */ + int data; /* Sensor data. + Negative if there was a read error */ + char data_buf[PMBUS_BLOCK_READ_SIZE]; +}; +#define to_pmbus_mfr(_attr) \ + container_of(_attr, struct pmbus_mfr, attribute) + +struct pmbus_boolean { + char name[PMBUS_NAME_SIZE]; /* sysfs boolean name */ + struct sensor_device_attribute attribute; + struct pmbus_sensor *s1; + struct pmbus_sensor *s2; +}; +#define to_pmbus_boolean(_attr) \ + container_of(_attr, struct pmbus_boolean, attribute) + +struct pmbus_label { + char name[PMBUS_NAME_SIZE]; /* sysfs label name */ + struct device_attribute attribute; + char label[PMBUS_NAME_SIZE]; /* label */ +}; +#define to_pmbus_label(_attr) \ + container_of(_attr, struct pmbus_label, attribute) + +struct pmbus_data { + struct device *dev; + struct device *hwmon_dev; + + u32 flags; /* from platform data */ + + int exponent[PMBUS_PAGES]; + /* linear mode: exponent for output voltages */ + + const struct pmbus_driver_info *info; + + int max_attributes; + int num_attributes; + struct attribute_group group; + const struct attribute_group *groups[2]; + + struct pmbus_sensor *sensors; + struct pmbus_mfr *mfr; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* + * A single status register covers multiple attributes, + * so we keep them all together. + */ + u8 status[PB_NUM_STATUS_REG]; + u8 status_register; + + u8 currpage; +}; + +void pmbus_clear_cache(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + + data->valid = false; +} + +int pmbus_set_page(struct i2c_client *client, u8 page) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int rv = 0; + int newpage; + + if (page != data->currpage) { + rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE); + if (newpage != page) + rv = -EIO; + else + data->currpage = page; + } + return rv; +} + +int pmbus_write_byte(struct i2c_client *client, int page, u8 value) +{ + int rv; + + if (page >= 0) { + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + } + + return i2c_smbus_write_byte(client, value); +} + +/* + * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_byte(struct i2c_client *client, int page, u8 value) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->write_byte) { + status = info->write_byte(client, page, value); + if (status != -ENODATA) + return status; + } + return pmbus_write_byte(client, page, value); +} + +int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word) +{ + int rv; + + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + + return i2c_smbus_write_word_data(client, reg, word); +} + +/* + * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->write_word_data) { + status = info->write_word_data(client, page, reg, word); + if (status != -ENODATA) + return status; + } + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + return pmbus_write_word_data(client, page, reg, word); +} + +int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg) +{ + int rv; + + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + + return i2c_smbus_read_word_data(client, reg); +} + +int pmbus_read_block_data(struct i2c_client *client, u8 reg, u8* value) +{ + return i2c_smbus_read_block_data(client, reg, value); +} + +static int _pmbus_read_block_data(struct i2c_client *client, int page, int reg, u8* value) +{ + int rv; + + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + return pmbus_read_block_data(client, reg, value); +} + +/* + * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->read_word_data) { + status = info->read_word_data(client, page, reg); + if (status != -ENODATA) + return status; + } + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + return pmbus_read_word_data(client, page, reg); +} + +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) +{ + int rv; + + if (page >= 0) { + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + } + + return i2c_smbus_read_byte_data(client, reg); +} + +/* + * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->read_byte_data) { + status = info->read_byte_data(client, page, reg); + if (status != -ENODATA) + return status; + } + return pmbus_read_byte_data(client, page, reg); +} + +static void pmbus_clear_fault_page(struct i2c_client *client, int page) +{ + _pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); +} + +void pmbus_clear_faults(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int i; + + for (i = 0; i < data->info->pages; i++) + pmbus_clear_fault_page(client, i); +} + +static int pmbus_check_status_cml(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int status, status2; + + status = _pmbus_read_byte_data(client, -1, data->status_register); + if (status < 0 || (status & PB_STATUS_CML)) { + status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); + if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND)) + return -EIO; + } + return 0; +} + +static bool pmbus_check_register(struct i2c_client *client, + int (*func)(struct i2c_client *client, + int page, int reg), + int page, int reg) +{ + int rv; + struct pmbus_data *data = i2c_get_clientdata(client); + + rv = func(client, page, reg); + if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) + rv = pmbus_check_status_cml(client); + pmbus_clear_fault_page(client, -1); + return rv >= 0; +} + +bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) +{ + return pmbus_check_register(client, _pmbus_read_byte_data, page, reg); +} + +bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) +{ + return pmbus_check_register(client, _pmbus_read_word_data, page, reg); +} + +const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + + return data->info; +} + +static struct _pmbus_status { + u32 func; + u16 base; + u16 reg; +} pmbus_status[] = { + { PMBUS_HAVE_STATUS_VOUT, PB_STATUS_VOUT_BASE, PMBUS_STATUS_VOUT }, + { PMBUS_HAVE_STATUS_IOUT, PB_STATUS_IOUT_BASE, PMBUS_STATUS_IOUT }, + { PMBUS_HAVE_STATUS_TEMP, PB_STATUS_TEMP_BASE, + PMBUS_STATUS_TEMPERATURE }, + { PMBUS_HAVE_STATUS_FAN12, PB_STATUS_FAN_BASE, PMBUS_STATUS_FAN_12 }, + { PMBUS_HAVE_STATUS_FAN34, PB_STATUS_FAN34_BASE, PMBUS_STATUS_FAN_34 }, +}; + +static struct _pmbus_status_reg { + char name[PMBUS_NAME_SIZE]; + u16 base; + u8 offset; +} pmbus_status_reg[] = { + { "vout_ov_fault", PB_STATUS_VOUT_BASE, PB_VOLTAGE_UV_FAULT}, + { "vout_uv_fault", PB_STATUS_VOUT_BASE, PB_VOLTAGE_OV_FAULT}, + { "ot_fault", PB_STATUS_TEMP_BASE, PB_TEMP_OT_FAULT}, + { "ot_warning", PB_STATUS_TEMP_BASE, PB_TEMP_OT_WARNING}, + { "vin_uv_warning", PB_STATUS_INPUT_BASE, PB_VOLTAGE_UV_WARNING}, + { "vin_uv_fault", PB_STATUS_INPUT_BASE, PB_VOLTAGE_UV_FAULT}, +}; + +static struct _pmbus_mfr_reg { + char name[PMBUS_NAME_SIZE]; + u16 reg; +} pmbus_mfr_reg[] = { + { "mfr_id", PMBUS_MFR_ID}, + { "mfr_model", PMBUS_MFR_MODEL}, + { "mfr_revision", PMBUS_MFR_REVISION}, + { "mfr_location", PMBUS_MFR_LOCATION}, + { "mfr_date", PMBUS_MFR_DATE}, + { "mfr_serial", PMBUS_MFR_SERIAL}, +}; + +static struct pmbus_data *pmbus_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + struct pmbus_sensor *sensor; + struct pmbus_mfr *mfr; + + mutex_lock(&data->update_lock); + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + int i, j; + + for (i = 0; i < info->pages; i++) { + data->status[PB_STATUS_BASE + i] + = _pmbus_read_byte_data(client, i, + data->status_register); + for (j = 0; j < ARRAY_SIZE(pmbus_status); j++) { + struct _pmbus_status *s = &pmbus_status[j]; + + if (!(info->func[i] & s->func)) + continue; + data->status[s->base + i] + = _pmbus_read_byte_data(client, i, + s->reg); + } + } + + if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) + data->status[PB_STATUS_INPUT_BASE] + = _pmbus_read_byte_data(client, 0, + PMBUS_STATUS_INPUT); + + if (info->func[0] & PMBUS_HAVE_STATUS_VMON) + data->status[PB_STATUS_VMON_BASE] + = _pmbus_read_byte_data(client, 0, + PMBUS_VIRT_STATUS_VMON); + + for (sensor = data->sensors; sensor; sensor = sensor->next) { + if (!data->valid || sensor->update) + sensor->data + = _pmbus_read_word_data(client, + sensor->page, + sensor->reg); + } + + /* read mfg data */ + for (mfr = data->mfr; mfr; mfr = mfr->next) { + mfr->data = _pmbus_read_block_data(client, mfr->page, mfr->reg, mfr->data_buf); + } + + pmbus_clear_faults(client); + data->last_updated = jiffies; + data->valid = 1; + } + mutex_unlock(&data->update_lock); + return data; +} + +/* + * Convert linear sensor values to milli- or micro-units + * depending on sensor type. + */ +static long pmbus_reg2data_linear(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + s16 exponent; + s32 mantissa; + long val; + + if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */ + exponent = data->exponent[sensor->page]; + mantissa = (u16) sensor->data; + } else { /* LINEAR11 */ + exponent = ((s16)sensor->data) >> 11; + mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5; + } + + val = mantissa; + + /* scale result to milli-units for all sensors except fans */ + if (sensor->class != PSC_FAN) + val = val * 1000L; + + /* scale result to micro-units for power sensors */ + if (sensor->class == PSC_POWER) + val = val * 1000L; + + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + return val; +} + +/* + * Convert direct sensor values to milli- or micro-units + * depending on sensor type. + */ +static long pmbus_reg2data_direct(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + long val = (s16) sensor->data; + long m, b, R; + + m = data->info->m[sensor->class]; + b = data->info->b[sensor->class]; + R = data->info->R[sensor->class]; + + if (m == 0) + return 0; + + /* X = 1/m * (Y * 10^-R - b) */ + R = -R; + /* scale result to milli-units for everything but fans */ + if (sensor->class != PSC_FAN) { + R += 3; + b *= 1000; + } + + /* scale result to micro-units for power sensors */ + if (sensor->class == PSC_POWER) { + R += 3; + b *= 1000; + } + + while (R > 0) { + val *= 10; + R--; + } + while (R < 0) { + val = DIV_ROUND_CLOSEST(val, 10); + R++; + } + + return (val - b) / m; +} + +/* + * Convert VID sensor values to milli- or micro-units + * depending on sensor type. + * We currently only support VR11. + */ +static long pmbus_reg2data_vid(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + long val = sensor->data; + + if (val < 0x02 || val > 0xb2) + return 0; + return DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100); +} + +static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) +{ + long val; + + switch (data->info->format[sensor->class]) { + case direct: + val = pmbus_reg2data_direct(data, sensor); + break; + case vid: + val = pmbus_reg2data_vid(data, sensor); + break; + case linear: + default: + val = pmbus_reg2data_linear(data, sensor); + break; + } + return val; +} + +#define MAX_MANTISSA (1023 * 1000) +#define MIN_MANTISSA (511 * 1000) + +static u16 pmbus_data2reg_linear(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + s16 exponent = 0, mantissa; + bool negative = false; + + /* simple case */ + if (val == 0) + return 0; + + if (sensor->class == PSC_VOLTAGE_OUT) { + /* LINEAR16 does not support negative voltages */ + if (val < 0) + return 0; + + /* + * For a static exponents, we don't have a choice + * but to adjust the value to it. + */ + if (data->exponent[sensor->page] < 0) + val <<= -data->exponent[sensor->page]; + else + val >>= data->exponent[sensor->page]; + val = DIV_ROUND_CLOSEST(val, 1000); + return val & 0xffff; + } + + if (val < 0) { + negative = true; + val = -val; + } + + /* Power is in uW. Convert to mW before converting. */ + if (sensor->class == PSC_POWER) + val = DIV_ROUND_CLOSEST(val, 1000L); + + /* + * For simplicity, convert fan data to milli-units + * before calculating the exponent. + */ + if (sensor->class == PSC_FAN) + val = val * 1000; + + /* Reduce large mantissa until it fits into 10 bit */ + while (val >= MAX_MANTISSA && exponent < 15) { + exponent++; + val >>= 1; + } + /* Increase small mantissa to improve precision */ + while (val < MIN_MANTISSA && exponent > -15) { + exponent--; + val <<= 1; + } + + /* Convert mantissa from milli-units to units */ + mantissa = DIV_ROUND_CLOSEST(val, 1000); + + /* Ensure that resulting number is within range */ + if (mantissa > 0x3ff) + mantissa = 0x3ff; + + /* restore sign */ + if (negative) + mantissa = -mantissa; + + /* Convert to 5 bit exponent, 11 bit mantissa */ + return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); +} + +static u16 pmbus_data2reg_direct(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + long m, b, R; + + m = data->info->m[sensor->class]; + b = data->info->b[sensor->class]; + R = data->info->R[sensor->class]; + + /* Power is in uW. Adjust R and b. */ + if (sensor->class == PSC_POWER) { + R -= 3; + b *= 1000; + } + + /* Calculate Y = (m * X + b) * 10^R */ + if (sensor->class != PSC_FAN) { + R -= 3; /* Adjust R and b for data in milli-units */ + b *= 1000; + } + val = val * m + b; + + while (R > 0) { + val *= 10; + R--; + } + while (R < 0) { + val = DIV_ROUND_CLOSEST(val, 10); + R++; + } + + return val; +} + +static u16 pmbus_data2reg_vid(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + val = clamp_val(val, 500, 1600); + + return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625); +} + +static u16 pmbus_data2reg(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + u16 regval; + + switch (data->info->format[sensor->class]) { + case direct: + regval = pmbus_data2reg_direct(data, sensor, val); + break; + case vid: + regval = pmbus_data2reg_vid(data, sensor, val); + break; + case linear: + default: + regval = pmbus_data2reg_linear(data, sensor, val); + break; + } + return regval; +} + +/* + * Return boolean calculated from converted data. + * defines a status register index and mask. + * The mask is in the lower 8 bits, the register index is in bits 8..23. + * + * The associated pmbus_boolean structure contains optional pointers to two + * sensor attributes. If specified, those attributes are compared against each + * other to determine if a limit has been exceeded. + * + * If the sensor attribute pointers are NULL, the function returns true if + * (status[reg] & mask) is true. + * + * If sensor attribute pointers are provided, a comparison against a specified + * limit has to be performed to determine the boolean result. + * In this case, the function returns true if v1 >= v2 (where v1 and v2 are + * sensor values referenced by sensor attribute pointers s1 and s2). + * + * To determine if an object exceeds upper limits, specify = . + * To determine if an object exceeds lower limits, specify = . + * + * If a negative value is stored in any of the referenced registers, this value + * reflects an error code which will be returned. + */ +static int pmbus_get_boolean(struct pmbus_data *data, struct pmbus_boolean *b, + int index) +{ + struct pmbus_sensor *s1 = b->s1; + struct pmbus_sensor *s2 = b->s2; + u16 reg = (index >> 8) & 0xffff; + u8 mask = index & 0xff; + int ret, status; + u8 regval; + + status = data->status[reg]; + if (status < 0) + return status; + + regval = status & mask; + if (!s1 && !s2) { + ret = !!regval; + } else if (!s1 || !s2) { + WARN(1, "Bad boolean descriptor %p: s1=%p, s2=%p\n", b, s1, s2); + return 0; + } else { + long v1, v2; + + if (s1->data < 0) + return s1->data; + if (s2->data < 0) + return s2->data; + + v1 = pmbus_reg2data(data, s1); + v2 = pmbus_reg2data(data, s2); + ret = !!(regval && v1 >= v2); + } + return ret; +} + +static ssize_t pmbus_show_boolean(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct pmbus_boolean *boolean = to_pmbus_boolean(attr); + struct pmbus_data *data = pmbus_update_device(dev); + int val; + + val = pmbus_get_boolean(data, boolean, attr->index); + if (val < 0) + return val; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t pmbus_show_sensor(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pmbus_data *data = pmbus_update_device(dev); + struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); + + if (sensor->data < 0) + return sensor->data; + + return snprintf(buf, PAGE_SIZE, "%ld\n", pmbus_reg2data(data, sensor)); +} + +static ssize_t pmbus_show_status(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pmbus_st *status = to_pmbus_status(attr); + struct pmbus_data *data = pmbus_update_device(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", !!(data->status[status->base] & attr->index)); +} + +static ssize_t pmbus_show_mfr(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pmbus_mfr *mfr; + + pmbus_update_device(dev); + mfr = to_pmbus_mfr(devattr); + + if (mfr->data < 0) + return mfr->data; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,16,49) + /* mfr->data[0] is data length, we don't want show data length when show command */ + return snprintf(buf, PAGE_SIZE, "%s\n", &mfr->data_buf[1]); +#else + return snprintf(buf, PAGE_SIZE, "%s\n", &mfr->data_buf); +#endif +} + +static ssize_t pmbus_set_sensor(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); + ssize_t rv = count; + long val = 0; + int ret; + u16 regval; + + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + regval = pmbus_data2reg(data, sensor, val); + ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval); + if (ret < 0) + rv = ret; + else + sensor->data = regval; + mutex_unlock(&data->update_lock); + return rv; +} + +static ssize_t pmbus_show_label(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct pmbus_label *label = to_pmbus_label(da); + + return snprintf(buf, PAGE_SIZE, "%s\n", label->label); +} + +static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr) +{ + if (data->num_attributes >= data->max_attributes - 1) { + int new_max_attrs = data->max_attributes + PMBUS_ATTR_ALLOC_SIZE; + void *new_attrs = krealloc(data->group.attrs, + new_max_attrs * sizeof(void *), + GFP_KERNEL); + if (!new_attrs) + return -ENOMEM; + data->group.attrs = new_attrs; + data->max_attributes = new_max_attrs; + } + + data->group.attrs[data->num_attributes++] = attr; + data->group.attrs[data->num_attributes] = NULL; + return 0; +} + +static void pmbus_dev_attr_init(struct device_attribute *dev_attr, + const char *name, + umode_t mode, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count)) +{ + sysfs_attr_init(&dev_attr->attr); + dev_attr->attr.name = name; + dev_attr->attr.mode = mode; + dev_attr->show = show; + dev_attr->store = store; +} + +static void pmbus_attr_init(struct sensor_device_attribute *a, + const char *name, + umode_t mode, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count), + int idx) +{ + pmbus_dev_attr_init(&a->dev_attr, name, mode, show, store); + a->index = idx; +} + +static int pmbus_add_boolean(struct pmbus_data *data, + const char *name, const char *type, int seq, + struct pmbus_sensor *s1, + struct pmbus_sensor *s2, + u16 reg, u8 mask) +{ + struct pmbus_boolean *boolean; + struct sensor_device_attribute *a; + + boolean = devm_kzalloc(data->dev, sizeof(*boolean), GFP_KERNEL); + if (!boolean) + return -ENOMEM; + + a = &boolean->attribute; + + snprintf(boolean->name, sizeof(boolean->name), "%s%d_%s", + name, seq, type); + boolean->s1 = s1; + boolean->s2 = s2; + pmbus_attr_init(a, boolean->name, S_IRUGO, pmbus_show_boolean, NULL, + (reg << 8) | mask); + + return pmbus_add_attribute(data, &a->dev_attr.attr); +} + +static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, + const char *name, const char *type, + int seq, int page, int reg, + enum pmbus_sensor_classes class, + bool update, bool readonly) +{ + struct pmbus_sensor *sensor; + struct device_attribute *a; + + sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return NULL; + a = &sensor->attribute; + + snprintf(sensor->name, sizeof(sensor->name), "%s%d_%s", + name, seq, type); + sensor->page = page; + sensor->reg = reg; + sensor->class = class; + sensor->update = update; + pmbus_dev_attr_init(a, sensor->name, + readonly ? S_IRUGO : S_IRUGO | S_IWUSR, + pmbus_show_sensor, pmbus_set_sensor); + + if (pmbus_add_attribute(data, &a->attr)) + return NULL; + + sensor->next = data->sensors; + data->sensors = sensor; + + return sensor; +} + +static int pmbus_add_status(struct pmbus_data *data, + const char *name, u16 base, + u8 offset) +{ + struct pmbus_st *status; + struct sensor_device_attribute *a; + + status = devm_kzalloc(data->dev, sizeof(*status), GFP_KERNEL); + if (!status) + return -ENOMEM; + + a = &status->attribute; + + snprintf(status->name, sizeof(status->name), "%s", name); + + status->base = base; + + pmbus_attr_init(a, status->name, S_IRUGO, + pmbus_show_status, NULL, offset); + + return pmbus_add_attribute(data, &a->dev_attr.attr); +} + +static struct pmbus_mfr *pmbus_add_mfr(struct pmbus_data *data, + const char *name, + int page, int reg) +{ + struct pmbus_mfr *mfr; + struct device_attribute *a; + + mfr = devm_kzalloc(data->dev, sizeof(*mfr), GFP_KERNEL); + if (!mfr) + return NULL; + a = &mfr->attribute; + + snprintf(mfr->name, sizeof(mfr->name), "%s", name); + mfr->page = page; + mfr->reg = reg; + pmbus_dev_attr_init(a, mfr->name, S_IRUGO, + pmbus_show_mfr, NULL); + + if (pmbus_add_attribute(data, &a->attr)) + return NULL; + + mfr->next = data->mfr; + data->mfr = mfr; + + return mfr; +} + +static int pmbus_add_label(struct pmbus_data *data, + const char *name, int seq, + const char *lstring, int index) +{ + struct pmbus_label *label; + struct device_attribute *a; + + label = devm_kzalloc(data->dev, sizeof(*label), GFP_KERNEL); + if (!label) + return -ENOMEM; + + a = &label->attribute; + + snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq); + if (!index) + strncpy(label->label, lstring, sizeof(label->label) - 1); + else + snprintf(label->label, sizeof(label->label), "%s%d", lstring, + index); + + pmbus_dev_attr_init(a, label->name, S_IRUGO, pmbus_show_label, NULL); + return pmbus_add_attribute(data, &a->attr); +} + +/* + * Search for attributes. Allocate sensors, booleans, and labels as needed. + */ + +/* + * The pmbus_limit_attr structure describes a single limit attribute + * and its associated alarm attribute. + */ +struct pmbus_limit_attr { + u16 reg; /* Limit register */ + u16 sbit; /* Alarm attribute status bit */ + bool update; /* True if register needs updates */ + bool low; /* True if low limit; for limits with compare + functions only */ + const char *attr; /* Attribute name */ + const char *alarm; /* Alarm attribute name */ +}; + +/* + * The pmbus_sensor_attr structure describes one sensor attribute. This + * description includes a reference to the associated limit attributes. + */ +struct pmbus_sensor_attr { + u16 reg; /* sensor register */ + u8 gbit; /* generic status bit */ + u8 nlimit; /* # of limit registers */ + enum pmbus_sensor_classes class;/* sensor class */ + const char *label; /* sensor label */ + bool paged; /* true if paged sensor */ + bool update; /* true if update needed */ + bool compare; /* true if compare function needed */ + u32 func; /* sensor mask */ + u32 sfunc; /* sensor status mask */ + int sbase; /* status base register */ + const struct pmbus_limit_attr *limit;/* limit registers */ +}; + +/* + * Add a set of limit attributes and, if supported, the associated + * alarm attributes. + * returns 0 if no alarm register found, 1 if an alarm register was found, + * < 0 on errors. + */ +static int pmbus_add_limit_attrs(struct i2c_client *client, + struct pmbus_data *data, + const struct pmbus_driver_info *info, + const char *name, int index, int page, + struct pmbus_sensor *base, + const struct pmbus_sensor_attr *attr) +{ + const struct pmbus_limit_attr *l = attr->limit; + int nlimit = attr->nlimit; + int have_alarm = 0; + int i, ret; + struct pmbus_sensor *curr; + + for (i = 0; i < nlimit; i++) { + if (pmbus_check_word_register(client, page, l->reg)) { + curr = pmbus_add_sensor(data, name, l->attr, index, + page, l->reg, attr->class, + attr->update || l->update, + false); + if (!curr) + return -ENOMEM; + if (l->sbit && (info->func[page] & attr->sfunc)) { + ret = pmbus_add_boolean(data, name, + l->alarm, index, + attr->compare ? l->low ? curr : base + : NULL, + attr->compare ? l->low ? base : curr + : NULL, + attr->sbase + page, l->sbit); + if (ret) + return ret; + have_alarm = 1; + } + } + l++; + } + return have_alarm; +} + +static int pmbus_add_sensor_attrs_one(struct i2c_client *client, + struct pmbus_data *data, + const struct pmbus_driver_info *info, + const char *name, + int index, int page, + const struct pmbus_sensor_attr *attr) +{ + struct pmbus_sensor *base; + int ret; + + if (attr->label) { + ret = pmbus_add_label(data, name, index, attr->label, + attr->paged ? page + 1 : 0); + if (ret) + return ret; + } + base = pmbus_add_sensor(data, name, "input", index, page, attr->reg, + attr->class, true, true); + if (!base) + return -ENOMEM; + if (attr->sfunc) { + ret = pmbus_add_limit_attrs(client, data, info, name, + index, page, base, attr); + if (ret < 0) + return ret; + /* + * Add generic alarm attribute only if there are no individual + * alarm attributes, if there is a global alarm bit, and if + * the generic status register for this page is accessible. + */ + if (!ret && attr->gbit && + pmbus_check_byte_register(client, page, + data->status_register)) { + ret = pmbus_add_boolean(data, name, "alarm", index, + NULL, NULL, + PB_STATUS_BASE + page, + attr->gbit); + if (ret) + return ret; + } + } + return 0; +} + +static int pmbus_add_sensor_attrs(struct i2c_client *client, + struct pmbus_data *data, + const char *name, + const struct pmbus_sensor_attr *attrs, + int nattrs) +{ + const struct pmbus_driver_info *info = data->info; + int index, i; + int ret; + + index = 1; + for (i = 0; i < nattrs; i++) { + int page, pages; + + pages = attrs->paged ? info->pages : 1; + for (page = 0; page < pages; page++) { + if (!(info->func[page] & attrs->func)) + continue; + ret = pmbus_add_sensor_attrs_one(client, data, info, + name, index, page, + attrs); + if (ret) + return ret; + index++; + } + attrs++; + } + return 0; +} + +static const struct pmbus_sensor_attr voltage_attributes[] = { + { + .reg = PMBUS_READ_VOUT, + .class = PSC_VOLTAGE_OUT, + .label = "vout", + .paged = true, + .func = PMBUS_HAVE_VOUT, + .sfunc = PMBUS_HAVE_STATUS_VOUT, + .sbase = PB_STATUS_VOUT_BASE, + .gbit = PB_STATUS_VOUT_OV, + } +}; + +static const struct pmbus_limit_attr iout_limit_attrs[] = { + { + .reg = PMBUS_IOUT_OC_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_IOUT_OC_WARNING, + }, { + .reg = PMBUS_IOUT_OC_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_IOUT_OC_FAULT, + } +}; + +static const struct pmbus_sensor_attr current_attributes[] = { + { + .reg = PMBUS_READ_IIN, + .class = PSC_CURRENT_IN, + .label = "iin", + .func = PMBUS_HAVE_IIN, + .sfunc = PMBUS_HAVE_STATUS_INPUT, + .sbase = PB_STATUS_INPUT_BASE, + }, { + .reg = PMBUS_READ_IOUT, + .class = PSC_CURRENT_OUT, + .label = "iout", + .paged = true, + .func = PMBUS_HAVE_IOUT, + .sfunc = PMBUS_HAVE_STATUS_IOUT, + .sbase = PB_STATUS_IOUT_BASE, + .gbit = PB_STATUS_IOUT_OC, + .limit = iout_limit_attrs, + .nlimit = ARRAY_SIZE(iout_limit_attrs), + } +}; + +/* Temperature atributes */ +static const struct pmbus_limit_attr temp_limit_attrs[] = { + { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + } +}; + +static const struct pmbus_limit_attr temp_limit_attrs2[] = { + { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + } +}; + +static const struct pmbus_limit_attr temp_limit_attrs3[] = { + { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + } +}; + +static const struct pmbus_sensor_attr temp_attributes[] = { + { + .reg = PMBUS_READ_TEMPERATURE_1, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sbase = PB_STATUS_TEMP_BASE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs, + .nlimit = ARRAY_SIZE(temp_limit_attrs), + }, { + .reg = PMBUS_READ_TEMPERATURE_2, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP2, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sbase = PB_STATUS_TEMP_BASE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs2, + .nlimit = ARRAY_SIZE(temp_limit_attrs2), + }, { + .reg = PMBUS_READ_TEMPERATURE_3, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP3, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sbase = PB_STATUS_TEMP_BASE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs3, + .nlimit = ARRAY_SIZE(temp_limit_attrs3), + } +}; + +static const int pmbus_fan_registers[] = { + PMBUS_READ_FAN_SPEED_1, +}; + +static const int pmbus_fan_config_registers[] = { + PMBUS_FAN_CONFIG_12, +}; + +static const int pmbus_fan_status_registers[] = { + PMBUS_STATUS_FAN_12, +}; + +static const u32 pmbus_fan_flags[] = { + PMBUS_HAVE_FAN12, +}; + +static const u32 pmbus_fan_status_flags[] = { + PMBUS_HAVE_STATUS_FAN12, +}; + +/* Fans */ +static int pmbus_add_fan_attributes(struct i2c_client *client, + struct pmbus_data *data) +{ + const struct pmbus_driver_info *info = data->info; + int index = 1; + int page; + int ret; + + for (page = 0; page < info->pages; page++) { + int f; + + for (f = 0; f < ARRAY_SIZE(pmbus_fan_registers); f++) { + int regval; + + if (!(info->func[page] & pmbus_fan_flags[f])) + break; + + if (!pmbus_check_word_register(client, page, + pmbus_fan_registers[f])) + break; + + /* + * Skip fan if not installed. + * Each fan configuration register covers multiple fans, + * so we have to do some magic. + */ + regval = _pmbus_read_byte_data(client, page, + pmbus_fan_config_registers[f]); + if (regval < 0 || + (!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4))))) + continue; + + if (pmbus_add_sensor(data, "fan", "input", index, + page, pmbus_fan_registers[f], + PSC_FAN, true, true) == NULL) + return -ENOMEM; + + /* + * Each fan status register covers multiple fans, + * so we have to do some magic. + */ + if ((info->func[page] & pmbus_fan_status_flags[f]) && + pmbus_check_byte_register(client, + page, pmbus_fan_status_registers[f])) { + int base; + + if (f > 1) /* fan 3, 4 */ + base = PB_STATUS_FAN34_BASE + page; + else + base = PB_STATUS_FAN_BASE + page; + ret = pmbus_add_boolean(data, "fan", + "alarm", index, NULL, NULL, base, + PB_FAN_FAN1_WARNING >> (f & 1)); + if (ret) + return ret; + ret = pmbus_add_boolean(data, "fan", + "fault", index, NULL, NULL, base, + PB_FAN_FAN1_FAULT >> (f & 1)); + if (ret) + return ret; + } + index++; + } + } + return 0; +} + +/* mfr items */ +static int pmbus_add_mfr_attributes(struct i2c_client *client, + struct pmbus_data *data) +{ + int page = 0; + int i; + + for(i = 0; i < ARRAY_SIZE(pmbus_mfr_reg);i++){ + + if (pmbus_add_mfr(data, pmbus_mfr_reg[i].name, page, pmbus_mfr_reg[i].reg) == NULL){ + return -ENOMEM; + } + } + + return 0; +} + +/* status items */ +static int pmbus_add_status_attributes(struct i2c_client *client, + struct pmbus_data *data) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(pmbus_status_reg);i++){ + if (pmbus_add_status(data, pmbus_status_reg[i].name, pmbus_status_reg[i].base, pmbus_status_reg[i].offset)){ + return -ENOMEM; + } + } + + return 0; +} + +static int pmbus_find_attributes(struct i2c_client *client, + struct pmbus_data *data) +{ + int ret; + + /* Voltage sensors */ + ret = pmbus_add_sensor_attrs(client, data, "in", voltage_attributes, + ARRAY_SIZE(voltage_attributes)); + if (ret) + return ret; + + /* Current sensors */ + ret = pmbus_add_sensor_attrs(client, data, "curr", current_attributes, + ARRAY_SIZE(current_attributes)); + if (ret) + return ret; + + /* Temperature sensors */ + ret = pmbus_add_sensor_attrs(client, data, "temp", temp_attributes, + ARRAY_SIZE(temp_attributes)); + if (ret) + return ret; + + /* Fans */ + ret = pmbus_add_fan_attributes(client, data); + if (ret) + return ret; + + /* mfr data */ + ret = pmbus_add_mfr_attributes(client, data); + if (ret) + return ret; + + /* status data */ + ret = pmbus_add_status_attributes(client, data); + return ret; +} + +/* + * Identify chip parameters. + * This function is called for all chips. + */ +static int pmbus_identify_common(struct i2c_client *client, + struct pmbus_data *data, int page) +{ + int vout_mode = -1; + + if (pmbus_check_byte_register(client, page, PMBUS_VOUT_MODE)) + vout_mode = _pmbus_read_byte_data(client, page, + PMBUS_VOUT_MODE); + if (vout_mode >= 0 && vout_mode != 0xff) { + /* + * Not all chips support the VOUT_MODE command, + * so a failure to read it is not an error. + */ + switch (vout_mode >> 5) { + case 0: /* linear mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != linear) + return -ENODEV; + + data->exponent[page] = ((s8)(vout_mode << 3)) >> 3; + break; + case 1: /* VID mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != vid) + return -ENODEV; + break; + case 2: /* direct mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != direct) + return -ENODEV; + break; + default: + return -ENODEV; + } + } + + pmbus_clear_fault_page(client, page); + return 0; +} + +static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, + struct pmbus_driver_info *info) +{ + struct device *dev = &client->dev; + int page, ret; + + /* + * Some PMBus chips don't support PMBUS_STATUS_BYTE, so try + * to use PMBUS_STATUS_WORD instead if that is the case. + * Bail out if both registers are not supported. + */ + data->status_register = PMBUS_STATUS_BYTE; + ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); + if (ret < 0 || ret == 0xff) { + data->status_register = PMBUS_STATUS_WORD; + ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); + if (ret < 0 || ret == 0xffff) { + dev_err(dev, "PMBus status register not found\n"); + return -ENODEV; + } + } + + pmbus_clear_faults(client); + + if (info->identify) { + ret = (*info->identify)(client, info); + if (ret < 0) { + dev_err(dev, "Chip identification failed\n"); + return ret; + } + } + + if (info->pages <= 0 || info->pages > PMBUS_PAGES) { + dev_err(dev, "Bad number of PMBus pages: %d\n", info->pages); + return -ENODEV; + } + + /* Current only using page 1 in BMS project */ + info->pages = 1; + + for (page = 0; page < info->pages; page++) { + ret = pmbus_identify_common(client, data, page); + if (ret < 0) { + dev_err(dev, "Failed to identify chip capabilities\n"); + return ret; + } + } + return 0; +} + +int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, + struct pmbus_driver_info *info) +{ + struct device *dev = &client->dev; + const struct pmbus_platform_data *pdata = dev_get_platdata(dev); + struct pmbus_data *data; + int ret; + + if (!info) + return -ENODEV; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE + | I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->dev = dev; + + if (pdata) + data->flags = pdata->flags; + data->info = info; + + ret = pmbus_init_common(client, data, info); + if (ret < 0) + return ret; + + ret = pmbus_find_attributes(client, data); + if (ret) + goto out_kfree; + + /* + * If there are no attributes, something is wrong. + * Bail out instead of trying to register nothing. + */ + if (!data->num_attributes) { + dev_err(dev, "No attributes found\n"); + ret = -ENODEV; + goto out_kfree; + } + + data->groups[0] = &data->group; + data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + dev_err(dev, "Failed to register hwmon device\n"); + goto out_kfree; + } + return 0; + +out_kfree: + kfree(data->group.attrs); + return ret; +} + +int pmbus_do_remove(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); + kfree(data->group.attrs); + return 0; +} + + +/* + * Find sensor groups and status registers on each page. + */ +static void pmbus_find_sensor_groups(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int page; + + /* Sensors detected on page 0 only */ + if (pmbus_check_word_register(client, 0, PMBUS_READ_VIN)) + info->func[0] |= PMBUS_HAVE_VIN; + if (pmbus_check_word_register(client, 0, PMBUS_READ_VCAP)) + info->func[0] |= PMBUS_HAVE_VCAP; + if (pmbus_check_word_register(client, 0, PMBUS_READ_IIN)) + info->func[0] |= PMBUS_HAVE_IIN; + if (pmbus_check_word_register(client, 0, PMBUS_READ_PIN)) + info->func[0] |= PMBUS_HAVE_PIN; + if (info->func[0] + && pmbus_check_byte_register(client, 0, PMBUS_STATUS_INPUT)) + info->func[0] |= PMBUS_HAVE_STATUS_INPUT; + if (pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_12) && + pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_1)) { + info->func[0] |= PMBUS_HAVE_FAN12; + if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_12)) + info->func[0] |= PMBUS_HAVE_STATUS_FAN12; + } + if (pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_34) && + pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_3)) { + info->func[0] |= PMBUS_HAVE_FAN34; + if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_34)) + info->func[0] |= PMBUS_HAVE_STATUS_FAN34; + } + if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_1)) + info->func[0] |= PMBUS_HAVE_TEMP; + if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_2)) + info->func[0] |= PMBUS_HAVE_TEMP2; + if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_3)) + info->func[0] |= PMBUS_HAVE_TEMP3; + if (info->func[0] & (PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 + | PMBUS_HAVE_TEMP3) + && pmbus_check_byte_register(client, 0, + PMBUS_STATUS_TEMPERATURE)) + info->func[0] |= PMBUS_HAVE_STATUS_TEMP; + + /* Sensors detected on all pages */ + for (page = 0; page < info->pages; page++) { + if (pmbus_check_word_register(client, page, PMBUS_READ_VOUT)) { + info->func[page] |= PMBUS_HAVE_VOUT; + if (pmbus_check_byte_register(client, page, + PMBUS_STATUS_VOUT)) + info->func[page] |= PMBUS_HAVE_STATUS_VOUT; + } + if (pmbus_check_word_register(client, page, PMBUS_READ_IOUT)) { + info->func[page] |= PMBUS_HAVE_IOUT; + if (pmbus_check_byte_register(client, 0, + PMBUS_STATUS_IOUT)) + info->func[page] |= PMBUS_HAVE_STATUS_IOUT; + } + if (pmbus_check_word_register(client, page, PMBUS_READ_POUT)) + info->func[page] |= PMBUS_HAVE_POUT; + } +} + +/* + * Identify chip parameters. + */ +static int pmbus_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int ret = 0; + + if (!info->pages) { + /* + * Check if the PAGE command is supported. If it is, + * keep setting the page number until it fails or until the + * maximum number of pages has been reached. Assume that + * this is the number of pages supported by the chip. + */ + if (pmbus_check_byte_register(client, 0, PMBUS_PAGE)) { + int page; + + for (page = 1; page < PMBUS_PAGES; page++) { + if (pmbus_set_page(client, page) < 0) + break; + } + pmbus_set_page(client, 0); + info->pages = page; + } else { + info->pages = 1; + } + } + + if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) { + int vout_mode; + + vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); + if (vout_mode >= 0 && vout_mode != 0xff) { + switch (vout_mode >> 5) { + case 0: + break; + case 1: + info->format[PSC_VOLTAGE_OUT] = vid; + break; + case 2: + info->format[PSC_VOLTAGE_OUT] = direct; + break; + default: + ret = -ENODEV; + goto abort; + } + } + } + + /* + * We should check if the COEFFICIENTS register is supported. + * If it is, and the chip is configured for direct mode, we can read + * the coefficients from the chip, one set per group of sensor + * registers. + * + * To do this, we will need access to a chip which actually supports the + * COEFFICIENTS command, since the command is too complex to implement + * without testing it. Until then, abort if a chip configured for direct + * mode was detected. + */ + if (info->format[PSC_VOLTAGE_OUT] == direct) { + ret = -ENODEV; + goto abort; + } + + /* Try to find sensor groups */ + pmbus_find_sensor_groups(client, info); +abort: + return ret; +} + +static int pmbus_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pmbus_driver_info *info; + + info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pages = id->driver_data; + info->identify = pmbus_identify; + + return pmbus_do_probe(client, id, info); +} + +/* + * Use driver_data to set the number of pages supported by the chip. + */ +static const struct i2c_device_id pmbus_id[] = { + {"fse000", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pmbus_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver pmbus_driver = { + .driver = { + .name = "fse000", + }, + .probe = pmbus_probe, + .remove = pmbus_do_remove, + .id_table = pmbus_id, +}; + +module_i2c_driver(pmbus_driver); + +MODULE_DESCRIPTION("mitac_ly1200_32x_fse000 driver"); +MODULE_AUTHOR("Eddy Weng "); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_gpe.c b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_gpe.c new file mode 100644 index 000000000000..3ced2b0a399b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_gpe.c @@ -0,0 +1,93 @@ +#include +#include + +#define BMS_GPE_CLASS "bms_acpi" +#define BMS_GPE_DRIVER_NAME "bms_gpe" +#define BMS_GPE_DEVICE_NAME "bms_acpi_gpe" + +static int bms_gpe[] = {0x01, 0x02, 0x47}; +static int bms_gpe_num = sizeof(bms_gpe) / sizeof(bms_gpe[0]); + +static u32 bms_gpe_handler(acpi_handle handle, u32 gpe_num, void *context) +{ + struct acpi_device *device = context; + + acpi_bus_generate_netlink_event(device->pnp.device_class,dev_name(&device->dev), + gpe_num, 0); + return ACPI_INTERRUPT_HANDLED; /* GPE will be disable afterward */ +} + +static int bms_gpe_add(struct acpi_device *device) +{ + acpi_status status; + int i = 0; + char info_str[60] = { 0 }; + char temp[6] = { 0 }; + + if (!device) { + printk("No device of BMS GPE\n"); + return -EINVAL; + } + + strcpy(acpi_device_name(device), BMS_GPE_DEVICE_NAME); + strcpy(acpi_device_class(device), BMS_GPE_CLASS); + + strncat(info_str, "Initialized GPE list = ", 23); + for (i = 0; i < bms_gpe_num; i++) { + status = acpi_install_gpe_handler(NULL, bms_gpe[i], + ACPI_GPE_LEVEL_TRIGGERED, + &bms_gpe_handler, device); + if (status != AE_OK) { + printk("Fail to claim BMS GPE%X (code:0x%X)\n",bms_gpe[i],status); + return -EINVAL; + } + snprintf(temp, sizeof(temp), "0x%.2X ", bms_gpe[i]); + strncat(info_str, temp, 6); + } + + dev_info(&device->dev, "%s.\n", info_str); + + return 0; +} + +static int bms_gpe_remove(struct acpi_device *device) +{ + int i = 0; + for (i = 0; i < bms_gpe_num; i++) { + acpi_remove_gpe_handler(NULL, bms_gpe[i], &bms_gpe_handler); + } + return 0; +} + +static const struct acpi_device_id bms_acpi_device_ids[] = { + { "PNP0C01", 0 }, + { /* END OF LIST */ } +}; + +static struct acpi_driver bms_gpe_driver = { + .name = BMS_GPE_DRIVER_NAME, + .class = BMS_GPE_CLASS, + .ids = bms_acpi_device_ids, + .ops = { + .add = bms_gpe_add, + .remove = bms_gpe_remove, + }, +}; + +static int __init bms_gpe_init(void) +{ + printk(KERN_INFO "%s: init.\n", __FUNCTION__); + return acpi_bus_register_driver(&bms_gpe_driver); +} + +static void __exit bms_gpe_exit(void) +{ + printk(KERN_INFO "%s: exit.\n", __FUNCTION__); + acpi_bus_unregister_driver(&bms_gpe_driver); +} + +module_init(bms_gpe_init); +module_exit(bms_gpe_exit); +MODULE_AUTHOR("Yencheng Lin "); +MODULE_DESCRIPTION("mitac_ly1200_32x_gpe driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_master_cpld.c b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_master_cpld.c new file mode 100644 index 000000000000..921a2ea424a3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_master_cpld.c @@ -0,0 +1,503 @@ +#include +#include + +#include "master_cpld_reg.h" +#include "master_cpld_sysfs.h" + +static int debug_flag = 0; + +struct master_cpld_data { + struct mutex lock; + + struct i2c_client *client; + struct device_attribute bin; +}; + + +static const struct i2c_device_id master_cpld_ids[] = { + { "master_cpld", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, master_cpld_ids); + +static int master_cpld_raw_read(struct device *dev, struct device_attribute *attr, char *buf, + int reg_offset, int reg_width, int fld_shift, int fld_width, int fld_mask, char* reg_name){ + unsigned int reg_val = 0, fld_val; + static int debug_flag; + struct master_cpld_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int err; + + if (reg_width != 8){ + printk("%s: Register table width setting failed.\n", reg_name); + return -EINVAL; + } + mutex_lock(&data->lock); + if ((err = i2c_smbus_read_byte_data(client, (u8)reg_offset)) < 0) { + /* CPLD read error condition */; + mutex_unlock(&data->lock); + printk("%s: i2c read failed, error code = %d.\n", reg_name, err); + return err; + } + reg_val = err; + if (debug_flag) { + printk("%s: reg_offset = %d, width = %d, cur value = 0x%x.\n", reg_name, reg_offset, reg_width, reg_val); + } + mutex_unlock(&data->lock); + if (fld_width == reg_width) {fld_val = reg_val & fld_mask;} + else {fld_val = (reg_val >> fld_shift) & fld_mask;} + return sprintf(buf, "0x%x\n", fld_val); +} + +static int master_cpld_raw_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count, + int reg_offset, int reg_width, int fld_shift, int fld_width, int fld_mask, char* reg_name){ + int ret_code; + unsigned int reg_val, fld_val; + unsigned long val; + static int debug_flag; + struct master_cpld_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + if (reg_width != 8){ + printk("%s: Register table width setting failed.\n", reg_name); + return -EINVAL; + } + /* Parse buf and store to fld_val */ + if ((ret_code = kstrtoul(buf, 16, &val))){ + printk("%s: Conversion value = %s failed, errno = %d.\n", reg_name, buf, ret_code); + return ret_code; + } + fld_val = (unsigned int)val; + mutex_lock(&data->lock); + if ((ret_code = i2c_smbus_read_byte_data(client, (u8)reg_offset)) < 0) { + /* Handle CPLD read error condition */; + mutex_unlock(&data->lock); + printk("%s: i2c read failed, error code = %d.\n", reg_name, ret_code); + return ret_code; + } + reg_val = ret_code; + if (debug_flag) { + printk("%s: offset = %d, width = %d, cur value = 0x%x.\n", reg_name, reg_offset, reg_width, reg_val); + } + if (fld_width == reg_width) {reg_val = fld_val & fld_mask;} + else {reg_val = (reg_val & ~(fld_mask << fld_shift)) | + ((fld_val & (fld_mask)) << fld_shift);} + if ((ret_code = i2c_smbus_write_byte_data(client, (u8)reg_offset, (u8)reg_val)) != 0) { + /* Handle CPLD write error condition */; + mutex_unlock(&data->lock); + printk("%s: i2c write failed, error code = %d.\n", reg_name, ret_code); + return ret_code; + } + else if (debug_flag) { + printk("%s: offset = %d, width = %d, new value = 0x%x.\n", reg_name, reg_offset, reg_width, reg_val); + } + mutex_unlock(&data->lock); + return count; +} + +/*--------------------special file for debug---------------------- */ +static ssize_t master_cpld_debug_read(struct device *dev, struct device_attribute *attr, + char *buf){ + + return sprintf(buf, "%d\n", debug_flag); +} + + +static ssize_t master_cpld_debug_write(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int temp; + int error; + + error = kstrtoint(buf, 10, &temp); + if (error){ + printk(KERN_INFO "%s: Conversion value = %s failed.\n", __FUNCTION__, buf); + return count; + } + debug_flag = temp; + + if(debug_flag) + printk("%s, debug_flag = %d\n", __FUNCTION__, debug_flag); + + return count; +} +SYSFS_MISC_RW_ATTR_DEF(debug, master_cpld_debug_read, master_cpld_debug_write) + + + + +/* ----------------define port group---------------------------- */ +static struct attribute *port1_attributes[] = { + SYSFS_ATTR_PTR(port1_present), /* register: zqsfp28_present_8_1_status */ + SYSFS_ATTR_PTR(port1_rst), /* register: zqsfp28_rst_8_1 */ + SYSFS_ATTR_PTR(port1_modsel), /* register: zqsfp28_modsel_8_1 */ + SYSFS_ATTR_PTR(port1_lpmode), /* register: zqsfp28_lpmode_8_1 */ + SYSFS_ATTR_PTR(port1_irq_status), /* register: zqsfp28_irq_8_1_status */ + SYSFS_ATTR_PTR(port1_irq_msk), /* register: zqsfp28_irq_msk_8_1_status */ + NULL +}; + + +static struct attribute *port2_attributes[] = { + SYSFS_ATTR_PTR(port2_present), /* register: zqsfp28_present_8_1_status */ + SYSFS_ATTR_PTR(port2_rst), /* register: zqsfp28_rst_8_1 */ + SYSFS_ATTR_PTR(port2_modsel), /* register: zqsfp28_modsel_8_1 */ + SYSFS_ATTR_PTR(port2_lpmode), /* register: zqsfp28_lpmode_8_1 */ + SYSFS_ATTR_PTR(port2_irq_status), /* register: zqsfp28_irq_8_1_status */ + SYSFS_ATTR_PTR(port2_irq_msk), /* register: zqsfp28_irq_msk_8_1_status */ + NULL +}; + + +static struct attribute *port3_attributes[] = { + SYSFS_ATTR_PTR(port3_present), /* register: zqsfp28_present_8_1_status */ + SYSFS_ATTR_PTR(port3_rst), /* register: zqsfp28_rst_8_1 */ + SYSFS_ATTR_PTR(port3_modsel), /* register: zqsfp28_modsel_8_1 */ + SYSFS_ATTR_PTR(port3_lpmode), /* register: zqsfp28_lpmode_8_1 */ + SYSFS_ATTR_PTR(port3_irq_status), /* register: zqsfp28_irq_8_1_status */ + SYSFS_ATTR_PTR(port3_irq_msk), /* register: zqsfp28_irq_msk_8_1_status */ + NULL +}; + +static struct attribute *port4_attributes[] = { + SYSFS_ATTR_PTR(port4_present), /* register: zqsfp28_present_8_1_status */ + SYSFS_ATTR_PTR(port4_rst), /* register: zqsfp28_rst_8_1 */ + SYSFS_ATTR_PTR(port4_modsel), /* register: zqsfp28_modsel_8_1 */ + SYSFS_ATTR_PTR(port4_lpmode), /* register: zqsfp28_lpmode_8_1 */ + SYSFS_ATTR_PTR(port4_irq_status), /* register: zqsfp28_irq_8_1_status */ + SYSFS_ATTR_PTR(port4_irq_msk), /* register: zqsfp28_irq_msk_8_1_status */ + NULL +}; + +static struct attribute *port5_attributes[] = { + SYSFS_ATTR_PTR(port5_present), /* register: zqsfp28_present_8_1_status */ + SYSFS_ATTR_PTR(port5_rst), /* register: zqsfp28_rst_8_1 */ + SYSFS_ATTR_PTR(port5_modsel), /* register: zqsfp28_modsel_8_1 */ + SYSFS_ATTR_PTR(port5_lpmode), /* register: zqsfp28_lpmode_8_1 */ + SYSFS_ATTR_PTR(port5_irq_status), /* register: zqsfp28_irq_8_1_status */ + SYSFS_ATTR_PTR(port5_irq_msk), /* register: zqsfp28_irq_msk_8_1_status */ + NULL +}; + +static struct attribute *port6_attributes[] = { + SYSFS_ATTR_PTR(port6_present), /* register: zqsfp28_present_8_1_status */ + SYSFS_ATTR_PTR(port6_rst), /* register: zqsfp28_rst_8_1 */ + SYSFS_ATTR_PTR(port6_modsel), /* register: zqsfp28_modsel_8_1 */ + SYSFS_ATTR_PTR(port6_lpmode), /* register: zqsfp28_lpmode_8_1 */ + SYSFS_ATTR_PTR(port6_irq_status), /* register: zqsfp28_irq_8_1_status */ + SYSFS_ATTR_PTR(port6_irq_msk), /* register: zqsfp28_irq_msk_8_1_status */ + NULL +}; + +static struct attribute *port7_attributes[] = { + SYSFS_ATTR_PTR(port7_present), /* register: zqsfp28_present_8_1_status */ + SYSFS_ATTR_PTR(port7_rst), /* register: zqsfp28_rst_8_1 */ + SYSFS_ATTR_PTR(port7_modsel), /* register: zqsfp28_modsel_8_1 */ + SYSFS_ATTR_PTR(port7_lpmode), /* register: zqsfp28_lpmode_8_1 */ + SYSFS_ATTR_PTR(port7_irq_status), /* register: zqsfp28_irq_8_1_status */ + SYSFS_ATTR_PTR(port7_irq_msk), /* register: zqsfp28_irq_msk_8_1_status */ + NULL +}; + +static struct attribute *port8_attributes[] = { + SYSFS_ATTR_PTR(port8_present), /* register: zqsfp28_present_8_1_status */ + SYSFS_ATTR_PTR(port8_rst), /* register: zqsfp28_rst_8_1 */ + SYSFS_ATTR_PTR(port8_modsel), /* register: zqsfp28_modsel_8_1 */ + SYSFS_ATTR_PTR(port8_lpmode), /* register: zqsfp28_lpmode_8_1 */ + SYSFS_ATTR_PTR(port8_irq_status), /* register: zqsfp28_irq_8_1_status */ + SYSFS_ATTR_PTR(port8_irq_msk), /* register: zqsfp28_irq_msk_8_1_status */ + NULL +}; + +static struct attribute *port9_attributes[] = { + SYSFS_ATTR_PTR(port9_present), /* register: zqsfp28_present_16_9_status */ + SYSFS_ATTR_PTR(port9_rst), /* register: zqsfp28_rst_16_9 */ + SYSFS_ATTR_PTR(port9_modsel), /* register: zqsfp28_modsel_16_9 */ + SYSFS_ATTR_PTR(port9_lpmode), /* register: zqsfp28_lpmode_16_9 */ + SYSFS_ATTR_PTR(port9_irq_status), /* register: zqsfp28_irq_16_9_status */ + SYSFS_ATTR_PTR(port9_irq_msk), /* register: zqsfp28_irq_msk_16_9_status */ + NULL +}; + +static struct attribute *port10_attributes[] = { + SYSFS_ATTR_PTR(port10_present), /* register: zqsfp28_present_16_9_status */ + SYSFS_ATTR_PTR(port10_rst), /* register: zqsfp28_rst_16_9 */ + SYSFS_ATTR_PTR(port10_modsel), /* register: zqsfp28_modsel_16_9 */ + SYSFS_ATTR_PTR(port10_lpmode), /* register: zqsfp28_lpmode_16_9 */ + SYSFS_ATTR_PTR(port10_irq_status), /* register: zqsfp28_irq_16_9_status */ + SYSFS_ATTR_PTR(port10_irq_msk), /* register: zqsfp28_irq_msk_16_9_status */ + NULL +}; + +static struct attribute *port11_attributes[] = { + SYSFS_ATTR_PTR(port11_present), /* register: zqsfp28_present_16_9_status */ + SYSFS_ATTR_PTR(port11_rst), /* register: zqsfp28_rst_16_9 */ + SYSFS_ATTR_PTR(port11_modsel), /* register: zqsfp28_modsel_16_9 */ + SYSFS_ATTR_PTR(port11_lpmode), /* register: zqsfp28_lpmode_16_9 */ + SYSFS_ATTR_PTR(port11_irq_status), /* register: zqsfp28_irq_16_9_status */ + SYSFS_ATTR_PTR(port11_irq_msk), /* register: zqsfp28_irq_msk_16_9_status */ + NULL +}; + +static struct attribute *port12_attributes[] = { + SYSFS_ATTR_PTR(port12_present), /* register: zqsfp28_present_16_9_status */ + SYSFS_ATTR_PTR(port12_rst), /* register: zqsfp28_rst_16_9 */ + SYSFS_ATTR_PTR(port12_modsel), /* register: zqsfp28_modsel_16_9 */ + SYSFS_ATTR_PTR(port12_lpmode), /* register: zqsfp28_lpmode_16_9 */ + SYSFS_ATTR_PTR(port12_irq_status), /* register: zqsfp28_irq_16_9_status */ + SYSFS_ATTR_PTR(port12_irq_msk), /* register: zqsfp28_irq_msk_16_9_status */ + NULL +}; + +static struct attribute *port13_attributes[] = { + SYSFS_ATTR_PTR(port13_present), /* register: zqsfp28_present_16_9_status */ + SYSFS_ATTR_PTR(port13_rst), /* register: zqsfp28_rst_16_9 */ + SYSFS_ATTR_PTR(port13_modsel), /* register: zqsfp28_modsel_16_9 */ + SYSFS_ATTR_PTR(port13_lpmode), /* register: zqsfp28_lpmode_16_9 */ + SYSFS_ATTR_PTR(port13_irq_status), /* register: zqsfp28_irq_16_9_status */ + SYSFS_ATTR_PTR(port13_irq_msk), /* register: zqsfp28_irq_msk_16_9_status */ + NULL +}; + +static struct attribute *port14_attributes[] = { + SYSFS_ATTR_PTR(port14_present), /* register: zqsfp28_present_16_9_status */ + SYSFS_ATTR_PTR(port14_rst), /* register: zqsfp28_rst_16_9 */ + SYSFS_ATTR_PTR(port14_modsel), /* register: zqsfp28_modsel_16_9 */ + SYSFS_ATTR_PTR(port14_lpmode), /* register: zqsfp28_lpmode_16_9 */ + SYSFS_ATTR_PTR(port14_irq_status), /* register: zqsfp28_irq_16_9_status */ + SYSFS_ATTR_PTR(port14_irq_msk), /* register: zqsfp28_irq_msk_16_9_status */ + NULL +}; + +static struct attribute *port15_attributes[] = { + SYSFS_ATTR_PTR(port15_present), /* register: zqsfp28_present_16_9_status */ + SYSFS_ATTR_PTR(port15_rst), /* register: zqsfp28_rst_16_9 */ + SYSFS_ATTR_PTR(port15_modsel), /* register: zqsfp28_modsel_16_9 */ + SYSFS_ATTR_PTR(port15_lpmode), /* register: zqsfp28_lpmode_16_9 */ + SYSFS_ATTR_PTR(port15_irq_status), /* register: zqsfp28_irq_16_9_status */ + SYSFS_ATTR_PTR(port15_irq_msk), /* register: zqsfp28_irq_msk_16_9_status */ + NULL +}; + +static struct attribute *port16_attributes[] = { + SYSFS_ATTR_PTR(port16_present), /* register: zqsfp28_present_16_9_status */ + SYSFS_ATTR_PTR(port16_rst), /* register: zqsfp28_rst_16_9 */ + SYSFS_ATTR_PTR(port16_modsel), /* register: zqsfp28_modsel_16_9 */ + SYSFS_ATTR_PTR(port16_lpmode), /* register: zqsfp28_lpmode_16_9 */ + SYSFS_ATTR_PTR(port16_irq_status), /* register: zqsfp28_irq_16_9_status */ + SYSFS_ATTR_PTR(port16_irq_msk), /* register: zqsfp28_irq_msk_16_9_status */ + NULL +}; + + +static const struct attribute_group master_cpld_port_group[] = { + {.attrs = port1_attributes, + .name = "port1",}, + {.attrs = port2_attributes, + .name = "port2",}, + {.attrs = port3_attributes, + .name = "port3",}, + {.attrs = port4_attributes, + .name = "port4",}, + {.attrs = port5_attributes, + .name = "port5",}, + {.attrs = port6_attributes, + .name = "port6",}, + {.attrs = port7_attributes, + .name = "port7",}, + {.attrs = port8_attributes, + .name = "port8",}, + {.attrs = port9_attributes, + .name = "port9",}, + {.attrs = port10_attributes, + .name = "port10",}, + {.attrs = port11_attributes, + .name = "port11",}, + {.attrs = port12_attributes, + .name = "port12",}, + {.attrs = port13_attributes, + .name = "port13",}, + {.attrs = port14_attributes, + .name = "port14",}, + {.attrs = port15_attributes, + .name = "port15",}, + {.attrs = port16_attributes, + .name = "port16",} +}; + +/* ----------------define misc group---------------------------- */ +static struct attribute *misc_attributes[] = { + SYSFS_ATTR_PTR(mjr_rev), /* register: mstr_cpld_rev */ + SYSFS_ATTR_PTR(mnr_rev), /* register: mstr_cpld_rev */ + + SYSFS_ATTR_PTR(scrtch_reg), /* register: mstr_cpld_gpr */ + + SYSFS_ATTR_PTR(brd_rev), /* register: mb_brd_rev_type */ + SYSFS_ATTR_PTR(brd_type), /* register: mb_brd_rev_type */ + + SYSFS_ATTR_PTR(mb_rst), /* register: mstr_srr */ + SYSFS_ATTR_PTR(npu_rst), /* register: mstr_srr */ + SYSFS_ATTR_PTR(mgmt_phy_rst), /* register: mstr_srr */ + + SYSFS_ATTR_PTR(mb_eeprom_wp), /* register: eeprom_wp */ + SYSFS_ATTR_PTR(cpld_spi_wp), /* register: eeprom_wp */ + SYSFS_ATTR_PTR(fan_eeprom_wp), /* register: eeprom_wp */ + + SYSFS_ATTR_PTR(ps2_int_msk), /* register: mstr_irq */ + SYSFS_ATTR_PTR(ps1_int_msk), /* register: mstr_irq */ + SYSFS_ATTR_PTR(usb_fault_msk), /* register: mstr_irq */ + SYSFS_ATTR_PTR(pcie_int_msk), /* register: mstr_irq */ + SYSFS_ATTR_PTR(fan_alert_int_msk), /* register: mstr_irq */ + SYSFS_ATTR_PTR(usb_fault), /* register: mstr_irq */ + SYSFS_ATTR_PTR(pcie_int), /* register: mstr_irq */ + SYSFS_ATTR_PTR(fan_alert_int), /* register: mstr_irq */ + SYSFS_ATTR_PTR(system_led_fld), /* register: mstr_irq */ + SYSFS_ATTR_PTR(power_led), /* register: mstr_irq */ + SYSFS_ATTR_PTR(fan_led), /* register: mstr_irq */ + SYSFS_ATTR_PTR(locate_led), /* register: mstr_irq */ + + SYSFS_ATTR_PTR(led_test), /* register: fan_tray_3_1_led */ + SYSFS_ATTR_PTR(fan_tray3_led), /* register: fan_tray_3_1_led */ + SYSFS_ATTR_PTR(fan_tray2_led), /* register: fan_tray_3_1_led */ + SYSFS_ATTR_PTR(fan_tray1_led), /* register: fan_tray_3_1_led */ + SYSFS_ATTR_PTR(fan_tray6_led), /* register: fan_tray_6_4_led */ + SYSFS_ATTR_PTR(fan_tray5_led), /* register: fan_tray_6_4_led */ + SYSFS_ATTR_PTR(fan_tray4_led), /* register: fan_tray_6_4_led */ + + SYSFS_ATTR_PTR(fan_tray6_present), /* register: fan_tray_status */ + SYSFS_ATTR_PTR(fan_tray5_present), /* register: fan_tray_status */ + SYSFS_ATTR_PTR(fan_tray4_present), /* register: fan_tray_status */ + SYSFS_ATTR_PTR(fan_tray3_present), /* register: fan_tray_status */ + SYSFS_ATTR_PTR(fan_tray2_present), /* register: fan_tray_status */ + SYSFS_ATTR_PTR(fan_tray1_present), /* register: fan_tray_status */ + + SYSFS_ATTR_PTR(fan_type6), /* register: fan_type_status */ + SYSFS_ATTR_PTR(fan_type5), /* register: fan_type_status */ + SYSFS_ATTR_PTR(fan_type4), /* register: fan_type_status */ + SYSFS_ATTR_PTR(fan_type3), /* register: fan_type_status */ + SYSFS_ATTR_PTR(fan_type2), /* register: fan_type_status */ + SYSFS_ATTR_PTR(fan_type1), /* register: fan_type_status */ + + SYSFS_ATTR_PTR(ps1_ps), /* register: psu_en_status */ + SYSFS_ATTR_PTR(ps1_pg), /* register: psu_en_status */ + SYSFS_ATTR_PTR(ps1_int), /* register: psu_en_status */ + SYSFS_ATTR_PTR(ps1_on), /* register: psu_en_status */ + SYSFS_ATTR_PTR(ps2_ps), /* register: psu_en_status */ + SYSFS_ATTR_PTR(ps2_pg), /* register: psu_en_status */ + SYSFS_ATTR_PTR(ps2_int), /* register: psu_en_status */ + SYSFS_ATTR_PTR(ps2_on), /* register: psu_en_status */ + + SYSFS_ATTR_PTR(usb1_vbus_en), /* register: mb_pwr_en_status */ + SYSFS_ATTR_PTR(v5p0_en), /* register: mb_pwr_en_status */ + SYSFS_ATTR_PTR(v3p3_en), /* register: mb_pwr_en_status */ + SYSFS_ATTR_PTR(vcc_1v8_en), /* register: mb_pwr_en_status */ + SYSFS_ATTR_PTR(mac_avs1v_en), /* register: mb_pwr_en_status */ + SYSFS_ATTR_PTR(mac1v_en), /* register: mb_pwr_en_status */ + SYSFS_ATTR_PTR(vcc_1v25_en), /* register: mb_pwr_en_status */ + + SYSFS_ATTR_PTR(vcc_3p3_cpld), /* register: mb_pwr_status */ + SYSFS_ATTR_PTR(vcc5v_pg), /* register: mb_pwr_status */ + SYSFS_ATTR_PTR(vcc3v3_pg), /* register: mb_pwr_status */ + SYSFS_ATTR_PTR(vcc_1v8_pg), /* register: mb_pwr_status */ + SYSFS_ATTR_PTR(mac_avs1v_pg), /* register: mb_pwr_status */ + SYSFS_ATTR_PTR(mac1v_pg), /* register: mb_pwr_status */ + SYSFS_ATTR_PTR(vcc_1v25_pg), /* register: mb_pwr_status */ + + SYSFS_ATTR_PTR(port_1_8_present), /* register: zqsfp28_present_8_1_status */ + SYSFS_ATTR_PTR(port_9_16_present), /* register: zqsfp28_present_16_9_status */ + SYSFS_ATTR_PTR(port_1_8_rst), /* register: zqsfp28_rst_8_1 */ + SYSFS_ATTR_PTR(port_9_16_rst), /* register: zqsfp28_rst_16_9 */ + SYSFS_ATTR_PTR(port_1_8_modsel), /* register: zqsfp28_modsel_8_1 */ + SYSFS_ATTR_PTR(port_9_16_modsel), /* register: zqsfp28_modsel_16_9 */ + SYSFS_ATTR_PTR(port_1_8_irq_status), /* register: zqsfp28_irq_8_1_status */ + SYSFS_ATTR_PTR(port_9_16_irq_status),/* register: zqsfp28_irq_16_9_status */ + SYSFS_ATTR_PTR(port_1_8_irq_msk), /* register: zqsfp28_irq_msk_8_1_status */ + SYSFS_ATTR_PTR(port_9_16_irq_msk), /* register: zqsfp28_irq_msk_16_9_status */ + SYSFS_ATTR_PTR(port_1_8_lpmode), /* register: zqsfp28_lpmode_8_1 */ + SYSFS_ATTR_PTR(port_9_16_lpmode), /* register: zqsfp28_lpmode_16_9 */ + + SYSFS_ATTR_PTR(fan_tray1_6_present), /* register: fan_tray_status */ + SYSFS_ATTR_PTR(psu_en_status_fld), /* register: psu_en_status*/ + + SYSFS_ATTR_PTR(debug), /* debug flag for print more messages */ + NULL +}; +static const struct attribute_group master_cpld_group_misc = { + .attrs = misc_attributes, +}; + +static int master_cpld_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct master_cpld_data *master_cpld; + int err, i; + int grp_number = (int)( sizeof(master_cpld_port_group) / sizeof(master_cpld_port_group[0])); + + /* allocate memory to master_cpld */ + master_cpld = devm_kzalloc(&client->dev, sizeof(struct master_cpld_data) , GFP_KERNEL); + + if (!master_cpld) + return -ENOMEM; + + mutex_init(&master_cpld->lock); + + for(i = 0 ; i < grp_number ; i++){ + err = sysfs_create_group(&client->dev.kobj, &master_cpld_port_group[i]); + if (err){ + printk("%s: Error creeat port group %d.\n", __FUNCTION__, i+1); + } + } + err = sysfs_create_group(&client->dev.kobj, &master_cpld_group_misc); + if (err){ + printk("%s: Error creeat misc group.\n", __FUNCTION__); + } + + master_cpld->client = client; + i2c_set_clientdata(client, master_cpld); + + printk(KERN_INFO "%s: Master CPLD LCMXO3LF created.\n", __FUNCTION__); + + return 0; + +} + +static int master_cpld_remove(struct i2c_client *client) +{ + int i; + int grp_number = (int)( sizeof(master_cpld_port_group) / sizeof(master_cpld_port_group[0])); + + for(i = 0 ; i < grp_number ; i++){ + sysfs_remove_group(&client->dev.kobj, &master_cpld_port_group[i]); + } + sysfs_remove_group(&client->dev.kobj, &master_cpld_group_misc); + + printk(KERN_INFO "%s: Master CPLD removed.\n", __FUNCTION__); + return 0; +} + +static struct i2c_driver master_cpld_driver = { + .driver = { + .name = "master_cpld", + .owner = THIS_MODULE, + }, + .probe = master_cpld_probe, + .remove = master_cpld_remove, + .id_table = master_cpld_ids, +}; + +static int __init master_cpld_init(void) +{ + printk(KERN_INFO "%s: init.\n", __FUNCTION__); + return i2c_add_driver(&master_cpld_driver); +} +module_init(master_cpld_init); + +static void __exit master_cpld_exit(void) +{ + printk(KERN_INFO "%s: exit.\n", __FUNCTION__); + i2c_del_driver(&master_cpld_driver); +} +module_exit(master_cpld_exit); + +MODULE_DESCRIPTION("mitac_ly1200_32x_master_cpld driver"); +MODULE_AUTHOR("Eddy Weng "); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_pb_i2c.c b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_pb_i2c.c new file mode 100644 index 000000000000..bad9260ab4ca --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_pb_i2c.c @@ -0,0 +1,137 @@ +#include +#include +#include "bms_i2c.h" + +#define BMS_PB_I2C_CLIENT_NUM 6 + +static struct i2c_client *bms_pb_clients[BMS_PB_I2C_CLIENT_NUM] = {NULL}; +static int bms_pb_client_index = 0; + +static int __init __find_i2c_adap(struct device *dev, void *data) +{ + const char *name = data; + static const char *prefix = "i2c-"; + struct i2c_adapter *adapter; + + if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0) + { + return 0; + } + adapter = to_i2c_adapter(dev); + + return (strncmp(adapter->name, name, strlen(name)) == 0); +} + +static int __init find_i2c_adapter_num(enum i2c_adapter_type type) +{ + struct device *dev = NULL; + struct i2c_adapter *adapter; + const char *name = bms_i2c_adapter_names[type]; + + /* find the adapter by name */ + dev = bus_find_device(&i2c_bus_type, NULL, (void *)name, + __find_i2c_adap); + if (!dev) { + pr_err("%s: i2c adapter %s not found on system.\n", + __func__, name); + return -ENODEV; + } + adapter = to_i2c_adapter(dev); + + return adapter->nr; +} + + +static int __init find_i2c_mux_adapter_num(int parent_num, int num) +{ + struct device *dev = NULL; + struct i2c_adapter *adapter; + char name[48]; + + snprintf(name, sizeof(name), "i2c-%d-mux (chan_id %d)", + parent_num, num); + /* find the adapter by name */ + dev = bus_find_device(&i2c_bus_type, NULL, (void *)name, + __find_i2c_adap); + if (!dev) { + pr_err("%s: i2c adapter %s not found on system.\n", + __func__, name); + return -ENODEV; + } + adapter = to_i2c_adapter(dev); + + return adapter->nr; +} + +static __init struct i2c_client *bms_pb_setup_eeprom_24c01( + struct i2c_adapter *adap, int addr) +{ + struct i2c_board_info info_spd = { + I2C_BOARD_INFO("24c01", addr), + }; + + return i2c_new_device(adap, &info_spd); +} + + +static int __init bms_pb_setup_devices(void) +{ + struct i2c_adapter *adap; + int adap_num; + int parent_num; + + parent_num = find_i2c_adapter_num(I2C_ADAPTER_ISMT); + if (parent_num < 0) + return parent_num; + + adap_num = find_i2c_mux_adapter_num(parent_num, I2C_STAGE1_MUX_CHAN3); + if (adap_num < 0) + return adap_num; + + adap = i2c_get_adapter(adap_num); + if (!adap) { + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + goto exit; + } + + bms_pb_clients[bms_pb_client_index++] = bms_pb_setup_eeprom_24c01(adap, 0x50); + bms_pb_clients[bms_pb_client_index++] = bms_pb_setup_eeprom_24c01(adap, 0x51); + +exit: + return 0; +} + +static int __init bms_pb_i2c_init(void) +{ + /* Initial bms_sb_slients array. */ + memset(bms_pb_clients, 0x0, BMS_PB_I2C_CLIENT_NUM); + + bms_pb_setup_devices(); + return 0; +} + + +static void __exit bms_pb_i2c_exit(void){ + int i; + + for (i=(bms_pb_client_index-1); i>=0; i--) { + if (bms_pb_clients[i]) { + i2c_unregister_device(bms_pb_clients[i]); + bms_pb_clients[i] = NULL; + } + } + + bms_pb_client_index = 0; + + +} + + +module_init(bms_pb_i2c_init); +module_exit(bms_pb_i2c_exit); + + +MODULE_DESCRIPTION("mitac_ly1200_32x_pb_i2c driver"); +MODULE_AUTHOR("Eddy Weng "); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_sb_i2c.c b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_sb_i2c.c new file mode 100644 index 000000000000..e2a9718863ff --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_sb_i2c.c @@ -0,0 +1,514 @@ +#include +#include +#include +#include +#include +#include +#include "bms_i2c.h" + +/* Don't include MAC_AVS_1V */ +#define BMS_SB_I2C_CLIENT_NUM 35 +#define BMS_SB_ADAPTER_BASE 2 +#define BMS_SB_STAGE2_MUX_BUS_BASE 6 + + +static struct i2c_client *bms_sb_clients[BMS_SB_I2C_CLIENT_NUM] = {NULL}; +static int bms_sb_client_index = 0; + +enum bms_sb_switch_stage2_mux0_bus { + I2C_STAGE2_MUX0_CHAN0 = 0, + I2C_STAGE2_MUX0_CHAN1, + I2C_STAGE2_MUX0_CHAN2, + I2C_STAGE2_MUX0_CHAN3, + I2C_STAGE2_MUX0_CHAN4, + I2C_STAGE2_MUX0_CHAN5, + I2C_STAGE2_MUX0_CHAN6, + I2C_STAGE2_MUX0_CHAN7, +}; + +enum bms_sb_switch_stage2_mux1_bus { + I2C_STAGE2_MUX1_CHAN8 = 0, + I2C_STAGE2_MUX1_CHAN9, + I2C_STAGE2_MUX1_CHAN10, + I2C_STAGE2_MUX1_CHAN11, + I2C_STAGE2_MUX1_CHAN12, + I2C_STAGE2_MUX1_CHAN13, + I2C_STAGE2_MUX1_CHAN14, + I2C_STAGE2_MUX1_CHAN15, +}; + +enum bms_sb_switch_stage2_mux2_bus { + I2C_STAGE2_MUX2_CHAN16 = 0, + I2C_STAGE2_MUX2_CHAN17, + I2C_STAGE2_MUX2_CHAN18, + I2C_STAGE2_MUX2_CHAN19, + I2C_STAGE2_MUX2_CHAN20, + I2C_STAGE2_MUX2_CHAN21, + I2C_STAGE2_MUX2_CHAN22, + I2C_STAGE2_MUX2_CHAN23, +}; + +enum bms_sb_switch_stage2_mux3_bus { + I2C_STAGE2_MUX3_CHAN24 = 0, + I2C_STAGE2_MUX3_CHAN25, + I2C_STAGE2_MUX3_CHAN26, + I2C_STAGE2_MUX3_CHAN27, + I2C_STAGE2_MUX3_CHAN28, + I2C_STAGE2_MUX3_CHAN29, + I2C_STAGE2_MUX3_CHAN30, + I2C_STAGE2_MUX3_CHAN31, +}; + +static struct pca954x_platform_mode pmode_pca9548_mux[] = { + { .adap_id = BMS_SB_ADAPTER_BASE + 0, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 1, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 2, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 3, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 4, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 5, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 6, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 7, }, +}; + +static struct pca954x_platform_data platdata_pca9548_mux = { + .modes = pmode_pca9548_mux, + .num_modes = ARRAY_SIZE(pmode_pca9548_mux), +}; + +static struct pca954x_platform_mode pmode_stage1_pca9548_mux0[] = { + { .adap_id = BMS_SB_ADAPTER_BASE + 8, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 9, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 10, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 11, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 12, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 13, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 14, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 15, }, +}; + +static struct pca954x_platform_data platdata_stage1_pca9548_mux0 = { + .modes = pmode_stage1_pca9548_mux0, + .num_modes = ARRAY_SIZE(pmode_stage1_pca9548_mux0), +}; + +static struct pca954x_platform_mode pmode_stage1_pca9548_mux1[] = { + { .adap_id = BMS_SB_ADAPTER_BASE + 16, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 17, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 18, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 19, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 20, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 21, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 22, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 23, }, +}; + +static struct pca954x_platform_data platdata_stage1_pca9548_mux1 = { + .modes = pmode_stage1_pca9548_mux1, + .num_modes = ARRAY_SIZE(pmode_stage1_pca9548_mux1), +}; + +static struct pca954x_platform_mode pmode_stage1_pca9548_mux2[] = { + { .adap_id = BMS_SB_ADAPTER_BASE + 24, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 25, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 26, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 27, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 28, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 29, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 30, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 31, }, +}; + +static struct pca954x_platform_data platdata_stage1_pca9548_mux2 = { + .modes = pmode_stage1_pca9548_mux2, + .num_modes = ARRAY_SIZE(pmode_stage1_pca9548_mux2), +}; + +static struct pca954x_platform_mode pmode_stage1_pca9548_mux3[] = { + { .adap_id = BMS_SB_ADAPTER_BASE + 32, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 33, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 34, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 35, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 36, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 37, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 38, }, + { .adap_id = BMS_SB_ADAPTER_BASE + 39, }, +}; + +static struct pca954x_platform_data platdata_stage1_pca9548_mux3 = { + .modes = pmode_stage1_pca9548_mux3, + .num_modes = ARRAY_SIZE(pmode_stage1_pca9548_mux3), +}; + +static int __init __find_i2c_adap(struct device *dev, void *data) +{ + const char *name = data; + static const char *prefix = "i2c-"; + struct i2c_adapter *adapter; + + if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0) + { + return 0; + } + adapter = to_i2c_adapter(dev); + + return (strncmp(adapter->name, name, strlen(name)) == 0); +} + +static int __init find_i2c_adapter_num(enum i2c_adapter_type type) +{ + struct device *dev = NULL; + struct i2c_adapter *adapter; + const char *name = bms_i2c_adapter_names[type]; + + /* find the adapter by name */ + dev = bus_find_device(&i2c_bus_type, NULL, (void *)name, + __find_i2c_adap); + if (!dev) { + pr_err("%s: i2c adapter %s not found on system.\n", + __func__, name); + return -ENODEV; + } + adapter = to_i2c_adapter(dev); + + return adapter->nr; +} + +static int __init find_i2c_mux_adapter_num(int parent_num, int num) +{ + struct device *dev = NULL; + struct i2c_adapter *adapter; + char name[48]; + + snprintf(name, sizeof(name), "i2c-%d-mux (chan_id %d)", + parent_num, num); + /* find the adapter by name */ + dev = bus_find_device(&i2c_bus_type, NULL, (void *)name, + __find_i2c_adap); + if (!dev) { + pr_err("%s: i2c adapter %s not found on system.\n", + __func__, name); + return -ENODEV; + } + adapter = to_i2c_adapter(dev); + + return adapter->nr; +} + +static __init struct i2c_client *bms_sb_setup_eeprom_24c04( + struct i2c_adapter *adap) +{ + struct i2c_board_info info_spd = { + I2C_BOARD_INFO("24c04", 0x50), + }; + + return i2c_new_device(adap, &info_spd); +} + +static __init struct i2c_client *bms_sb_setup_tmp75( + struct i2c_adapter *adap, int addr) +{ + struct i2c_board_info info_spd = { + I2C_BOARD_INFO("tmp75", addr), + }; + + return i2c_new_device(adap, &info_spd); +} + +static __init struct i2c_client *bms_sb_setup_switch(struct i2c_adapter *adap) +{ + struct i2c_board_info info = { + I2C_BOARD_INFO("pca9548", 0x70), + .platform_data = &platdata_pca9548_mux, + }; + + return i2c_new_device(adap, &info); +} + +static __init struct i2c_client *bms_sb_setup_stage1_mux0(struct i2c_adapter *adap) +{ + struct i2c_board_info info = { + I2C_BOARD_INFO("pca9548", 0x71), + .platform_data = &platdata_stage1_pca9548_mux0, + }; + + return i2c_new_device(adap, &info); +} + +static __init struct i2c_client *bms_sb_setup_stage1_mux1(struct i2c_adapter *adap) +{ + struct i2c_board_info info = { + I2C_BOARD_INFO("pca9548", 0x72), + .platform_data = &platdata_stage1_pca9548_mux1, + }; + + return i2c_new_device(adap, &info); +} + +static __init struct i2c_client *bms_sb_setup_stage1_mux2(struct i2c_adapter *adap) +{ + struct i2c_board_info info = { + I2C_BOARD_INFO("pca9548", 0x73), + .platform_data = &platdata_stage1_pca9548_mux2, + }; + + return i2c_new_device(adap, &info); +} + +static __init struct i2c_client *bms_sb_setup_stage1_mux3(struct i2c_adapter *adap) +{ + struct i2c_board_info info = { + I2C_BOARD_INFO("pca9548", 0x74), + .platform_data = &platdata_stage1_pca9548_mux3, + }; + + return i2c_new_device(adap, &info); +} + +static __init struct i2c_client *bms_sb_setup_slave_cpld(struct i2c_adapter *adap) +{ + struct i2c_board_info info = { + I2C_BOARD_INFO("slave_cpld", 0x33), + }; + + return i2c_new_device(adap, &info); +} + +static __init struct i2c_client *bms_sb_setup_master_cpld(struct i2c_adapter *adap) +{ + struct i2c_board_info info = { + I2C_BOARD_INFO("master_cpld", 0x32), + }; + + return i2c_new_device(adap, &info); +} + +static __init struct i2c_client *bms_sb_setup_sff8436(struct i2c_adapter *adap) +{ + struct i2c_board_info info = { + I2C_BOARD_INFO("sff8436", 0x50), + }; + + return i2c_new_device(adap, &info); +} + +static int __init bms_sb_setup_devices_ismt(void) +{ + struct i2c_adapter *adap; + int adap_num = find_i2c_adapter_num(I2C_ADAPTER_ISMT); + + if (adap_num < 0) + return adap_num; + + adap = i2c_get_adapter(adap_num); + if (!adap) { + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + return 0; + } + + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_switch(adap); + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_master_cpld(adap); + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_slave_cpld(adap); + + return 0; +} + +static int __init bms_sb_setup_devices_stage1(void) +{ + struct i2c_adapter *adap; + int adap_num; + int parent_num; + + parent_num = find_i2c_adapter_num(I2C_ADAPTER_ISMT); + if (parent_num < 0) + return parent_num; + + /* Mux chan0 steup */ + adap_num = find_i2c_mux_adapter_num(parent_num, I2C_STAGE1_MUX_CHAN0); + if (adap_num >= 0){ + adap = i2c_get_adapter(adap_num); + if(adap) { + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_eeprom_24c04(adap); + }else{ + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + } + }else{ + pr_err("%s failed to find i2c mux adap number %d.\n", __func__, I2C_STAGE1_MUX_CHAN0); + } + + /* Mux chan1 connect to fan board */ + + /* Mux chan2 steup */ + adap_num = find_i2c_mux_adapter_num(parent_num, I2C_STAGE1_MUX_CHAN2); + if (adap_num >= 0){ + adap = i2c_get_adapter(adap_num); + if(adap) { + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_tmp75(adap, 0x4a); + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_tmp75(adap, 0x4b); + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_tmp75(adap, 0x4c); + }else{ + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + } + }else{ + pr_err("%s failed to find i2c mux adap number %d.\n", __func__, I2C_STAGE1_MUX_CHAN2); + } + + /* Mux chan3 connect to power board */ + + /* Mux chan4 setup for i2c mux0 */ + adap_num = find_i2c_mux_adapter_num(parent_num, I2C_STAGE1_MUX_CHAN4); + if (adap_num >= 0){ + adap = i2c_get_adapter(adap_num); + if(adap) { + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_stage1_mux0(adap); + }else{ + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + } + }else{ + pr_err("%s failed to find i2c mux adap number %d.\n", __func__, I2C_STAGE1_MUX_CHAN4); + } + + /* Mux chan5 setup for i2c mux1 */ + adap_num = find_i2c_mux_adapter_num(parent_num, I2C_STAGE1_MUX_CHAN5); + if (adap_num >= 0){ + adap = i2c_get_adapter(adap_num); + if(adap) { + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_stage1_mux1(adap); + }else{ + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + } + }else{ + pr_err("%s failed to find i2c mux adap number %d.\n", __func__, I2C_STAGE1_MUX_CHAN5); + } + + /* Mux chan6 setup for i2c mux2 */ + adap_num = find_i2c_mux_adapter_num(parent_num, I2C_STAGE1_MUX_CHAN6); + if (adap_num >= 0){ + adap = i2c_get_adapter(adap_num); + if(adap) { + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_stage1_mux2(adap); + }else{ + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + } + }else{ + pr_err("%s failed to find i2c mux adap number %d.\n", __func__, I2C_STAGE1_MUX_CHAN6); + } + + /* Mux chan7 setup for i2c mux3 */ + adap_num = find_i2c_mux_adapter_num(parent_num, I2C_STAGE1_MUX_CHAN7); + if (adap_num >= 0){ + adap = i2c_get_adapter(adap_num); + if(adap) { + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_stage1_mux3(adap); + }else{ + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + } + }else{ + pr_err("%s failed to find i2c mux adap number %d.\n", __func__, I2C_STAGE1_MUX_CHAN7); + } + + return 0; +} + +static int __init bms_sb_setup_devices_stage2(void) +{ + struct i2c_adapter *adap; + int adap_num; + int i2c_base = sizeof(bms_i2c_adapter_names) / sizeof(bms_i2c_adapter_names[0]); + int i; + + /* stage2 mux0 chan0~7 install sff8436 */ + for(i = I2C_STAGE2_MUX0_CHAN0; i <= I2C_STAGE2_MUX0_CHAN7; i++){ + adap_num = find_i2c_mux_adapter_num(I2C_STAGE1_MUX_CHAN4 + i2c_base, I2C_STAGE2_MUX0_CHAN0 + i); + if (adap_num >= 0){ + adap = i2c_get_adapter(adap_num); + if(adap) { + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_sff8436(adap); + }else{ + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + } + }else{ + pr_err("%s failed to find i2c mux adap number %d.\n", __func__, I2C_STAGE1_MUX_CHAN4 + i2c_base); + } + } + + /* stage2 mux1 chan0~7 install sff8436 */ + for(i = I2C_STAGE2_MUX1_CHAN8; i <= I2C_STAGE2_MUX1_CHAN15; i++){ + adap_num = find_i2c_mux_adapter_num(I2C_STAGE1_MUX_CHAN5 + i2c_base, I2C_STAGE2_MUX1_CHAN8 + i); + if (adap_num >= 0){ + adap = i2c_get_adapter(adap_num); + if(adap) { + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_sff8436(adap); + }else{ + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + } + }else{ + pr_err("%s failed to find i2c mux adap number %d.\n", __func__, I2C_STAGE1_MUX_CHAN5 + i2c_base); + } + } + + /* stage2 mux2 chan0~7 install sff8436 */ + for(i = I2C_STAGE2_MUX2_CHAN16; i <= I2C_STAGE2_MUX2_CHAN23; i++){ + adap_num = find_i2c_mux_adapter_num(I2C_STAGE1_MUX_CHAN6 + i2c_base, I2C_STAGE2_MUX2_CHAN16 + i); + if (adap_num >= 0){ + adap = i2c_get_adapter(adap_num); + if(adap) { + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_sff8436(adap); + }else{ + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + } + }else{ + pr_err("%s failed to find i2c mux adap number %d.\n", __func__, I2C_STAGE1_MUX_CHAN6 + i2c_base); + } + } + + /* stage2 mux3 chan0~7 install sff8436 */ + for(i = I2C_STAGE2_MUX3_CHAN24; i <= I2C_STAGE2_MUX3_CHAN31; i++){ + adap_num = find_i2c_mux_adapter_num(I2C_STAGE1_MUX_CHAN7 + i2c_base, I2C_STAGE2_MUX3_CHAN24 + i); + if (adap_num >= 0){ + adap = i2c_get_adapter(adap_num); + if(adap) { + bms_sb_clients[bms_sb_client_index++] = bms_sb_setup_sff8436(adap); + }else{ + pr_err("%s failed to get i2c adap %d.\n", __func__, adap_num); + } + }else{ + pr_err("%s failed to find i2c mux adap number %d.\n", __func__, I2C_STAGE1_MUX_CHAN7 + i2c_base); + } + } +} + +static int __init bms_sb_i2c_init(void) +{ + /* Initial bms_sb_slients array. */ + memset(bms_sb_clients, 0x0, BMS_SB_I2C_CLIENT_NUM); + + bms_sb_setup_devices_ismt(); + mdelay(200); + bms_sb_setup_devices_stage1(); + mdelay(200); + bms_sb_setup_devices_stage2(); + + return 0; +} + +static void __exit bms_sb_i2c_exit(void){ + int i; + + for (i=(bms_sb_client_index-1); i>=0; i--) { + if (bms_sb_clients[i]) { + i2c_unregister_device(bms_sb_clients[i]); + bms_sb_clients[i] = NULL; + } + } + + bms_sb_client_index = 0; + +} + +module_init(bms_sb_i2c_init); +module_exit(bms_sb_i2c_exit); + +MODULE_DESCRIPTION("mitac_ly1200_32x_sb_i2c driver"); +MODULE_AUTHOR("Eddy Weng "); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_slave_cpld.c b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_slave_cpld.c new file mode 100644 index 000000000000..8809573fa4ad --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_slave_cpld.c @@ -0,0 +1,427 @@ +#include +#include + +#include "slave_cpld_reg.h" +#include "slave_cpld_sysfs.h" + +static int debug_flag = 0; + +struct slave_cpld_data { + struct mutex lock; + + struct i2c_client *client; + struct device_attribute bin; +}; + + +static const struct i2c_device_id slave_cpld_ids[] = { + { "slave_cpld", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, slave_cpld_ids); + +static int slave_cpld_raw_read(struct device *dev, struct device_attribute *attr, char *buf, + int reg_offset, int reg_width, int fld_shift, int fld_width, int fld_mask, char* reg_name){ + unsigned int reg_val = 0, fld_val; + static int debug_flag; + struct slave_cpld_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int err; + + if (reg_width != 8){ + printk("%s: Register table width setting failed.\n", reg_name); + return -EINVAL; + } + mutex_lock(&data->lock); + if ((err = i2c_smbus_read_byte_data(client, (u8)reg_offset)) < 0) { + /* CPLD read error condition */; + mutex_unlock(&data->lock); + printk("%s: i2c read failed, error code = %d.\n", reg_name, err); + return err; + } + reg_val = err; + if (debug_flag) { + printk("%s: reg_offset = %d, width = %d, cur value = 0x%x.\n", reg_name, reg_offset, reg_width, reg_val); + } + mutex_unlock(&data->lock); + if (fld_width == reg_width) {fld_val = reg_val & fld_mask;} + else {fld_val = (reg_val >> fld_shift) & fld_mask;} + return sprintf(buf, "0x%x\n", fld_val); +} + +static int slave_cpld_raw_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count, + int reg_offset, int reg_width, int fld_shift, int fld_width, int fld_mask, char* reg_name){ + int ret_code; + unsigned int reg_val, fld_val; + unsigned long val; + static int debug_flag; + struct slave_cpld_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + if (reg_width != 8){ + printk("%s: Register table width setting failed.\n", reg_name); + return -EINVAL; + } + /* Parse buf and store to fld_val */ + if ((ret_code = kstrtoul(buf, 16, &val))){ + printk("%s: Conversion value = %s failed, errno = %d.\n", reg_name, buf, ret_code); + return ret_code; + } + fld_val = (unsigned int)val; + mutex_lock(&data->lock); + if ((ret_code = i2c_smbus_read_byte_data(client, (u8)reg_offset)) < 0) { + /* Handle CPLD read error condition */; + mutex_unlock(&data->lock); + printk("%s: i2c read failed, error code = %d.\n", reg_name, ret_code); + return ret_code; + } + reg_val = ret_code; + if (debug_flag) { + printk("%s: offset = %d, width = %d, cur value = 0x%x.\n", reg_name, reg_offset, reg_width, reg_val); + } + if (fld_width == reg_width) {reg_val = fld_val & fld_mask;} + else {reg_val = (reg_val & ~(fld_mask << fld_shift)) | + ((fld_val & (fld_mask)) << fld_shift);} + if ((ret_code = i2c_smbus_write_byte_data(client, (u8)reg_offset, (u8)reg_val)) != 0) { + /* Handle CPLD write error condition */; + mutex_unlock(&data->lock); + printk("%s: i2c write failed, error code = %d.\n", reg_name, ret_code); + return ret_code; + } + else if (debug_flag) { + printk("%s: offset = %d, width = %d, new value = 0x%x.\n", reg_name, reg_offset, reg_width, reg_val); + } + mutex_unlock(&data->lock); + return count; +} + +/*--------------------special file for debug---------------------- */ +static ssize_t slave_cpld_debug_read(struct device *dev, struct device_attribute *attr, + char *buf){ + + return sprintf(buf, "%d\n", debug_flag); +} + + +static ssize_t slave_cpld_debug_write(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int temp; + int error; + + error = kstrtoint(buf, 10, &temp); + if (error){ + printk(KERN_INFO "%s: Conversion value = %s failed.\n", __FUNCTION__, buf); + return count; + } + debug_flag = temp; + + if(debug_flag) + printk("%s, debug_flag = %d\n", __FUNCTION__, debug_flag); + + return count; +} +SYSFS_MISC_RW_ATTR_DEF(debug, slave_cpld_debug_read, slave_cpld_debug_write) + + + + +/* ----------------define port group---------------------------- */ +static struct attribute *port17_attributes[] = { + SYSFS_ATTR_PTR(port17_present), /* register: zqsfp28_present_24_17_status */ + SYSFS_ATTR_PTR(port17_rst), /* register: zqsfp28_rst_24_17 */ + SYSFS_ATTR_PTR(port17_modsel), /* register: zqsfp28_modsel_24_17 */ + SYSFS_ATTR_PTR(port17_lpmode), /* register: zqsfp28_lpmode_24_17 */ + SYSFS_ATTR_PTR(port17_irq_status), /* register: zqsfp28_irq_24_17_status */ + SYSFS_ATTR_PTR(port17_irq_msk), /* register: zqsfp28_irq_msk_24_17_status */ + NULL +}; + +static struct attribute *port18_attributes[] = { + SYSFS_ATTR_PTR(port18_present), /* register: zqsfp28_present_24_17_status */ + SYSFS_ATTR_PTR(port18_rst), /* register: zqsfp28_rst_24_17 */ + SYSFS_ATTR_PTR(port18_modsel), /* register: zqsfp28_modsel_24_17 */ + SYSFS_ATTR_PTR(port18_lpmode), /* register: zqsfp28_lpmode_24_17 */ + SYSFS_ATTR_PTR(port18_irq_status), /* register: zqsfp28_irq_24_17_status */ + SYSFS_ATTR_PTR(port18_irq_msk), /* register: zqsfp28_irq_msk_24_17_status */ + NULL +}; + +static struct attribute *port19_attributes[] = { + SYSFS_ATTR_PTR(port19_present), /* register: zqsfp28_present_24_17_status */ + SYSFS_ATTR_PTR(port19_rst), /* register: zqsfp28_rst_24_17 */ + SYSFS_ATTR_PTR(port19_modsel), /* register: zqsfp28_modsel_24_17 */ + SYSFS_ATTR_PTR(port19_lpmode), /* register: zqsfp28_lpmode_24_17 */ + SYSFS_ATTR_PTR(port19_irq_status), /* register: zqsfp28_irq_24_17_status */ + SYSFS_ATTR_PTR(port19_irq_msk), /* register: zqsfp28_irq_msk_24_17_status */ + NULL +}; + +static struct attribute *port20_attributes[] = { + SYSFS_ATTR_PTR(port20_present), /* register: zqsfp28_present_24_17_status */ + SYSFS_ATTR_PTR(port20_rst), /* register: zqsfp28_rst_24_17 */ + SYSFS_ATTR_PTR(port20_modsel), /* register: zqsfp28_modsel_24_17 */ + SYSFS_ATTR_PTR(port20_lpmode), /* register: zqsfp28_lpmode_24_17 */ + SYSFS_ATTR_PTR(port20_irq_status), /* register: zqsfp28_irq_24_17_status */ + SYSFS_ATTR_PTR(port20_irq_msk), /* register: zqsfp28_irq_msk_24_17_status */ + NULL +}; + +static struct attribute *port21_attributes[] = { + SYSFS_ATTR_PTR(port21_present), /* register: zqsfp28_present_24_17_status */ + SYSFS_ATTR_PTR(port21_rst), /* register: zqsfp28_rst_24_17 */ + SYSFS_ATTR_PTR(port21_modsel), /* register: zqsfp28_modsel_24_17 */ + SYSFS_ATTR_PTR(port21_lpmode), /* register: zqsfp28_lpmode_24_17 */ + SYSFS_ATTR_PTR(port21_irq_status), /* register: zqsfp28_irq_24_17_status */ + SYSFS_ATTR_PTR(port21_irq_msk), /* register: zqsfp28_irq_msk_24_17_status */ + NULL +}; + +static struct attribute *port22_attributes[] = { + SYSFS_ATTR_PTR(port22_present), /* register: zqsfp28_present_24_17_status */ + SYSFS_ATTR_PTR(port22_rst), /* register: zqsfp28_rst_24_17 */ + SYSFS_ATTR_PTR(port22_modsel), /* register: zqsfp28_modsel_24_17 */ + SYSFS_ATTR_PTR(port22_lpmode), /* register: zqsfp28_lpmode_24_17 */ + SYSFS_ATTR_PTR(port22_irq_status), /* register: zqsfp28_irq_24_17_status */ + SYSFS_ATTR_PTR(port22_irq_msk), /* register: zqsfp28_irq_msk_24_17_status */ + NULL +}; + +static struct attribute *port23_attributes[] = { + SYSFS_ATTR_PTR(port23_present), /* register: zqsfp28_present_24_17_status */ + SYSFS_ATTR_PTR(port23_rst), /* register: zqsfp28_rst_24_17 */ + SYSFS_ATTR_PTR(port23_modsel), /* register: zqsfp28_modsel_24_17 */ + SYSFS_ATTR_PTR(port23_lpmode), /* register: zqsfp28_lpmode_24_17 */ + SYSFS_ATTR_PTR(port23_irq_status), /* register: zqsfp28_irq_24_17_status */ + SYSFS_ATTR_PTR(port23_irq_msk), /* register: zqsfp28_irq_msk_24_17_status */ + NULL +}; + +static struct attribute *port24_attributes[] = { + SYSFS_ATTR_PTR(port24_present), /* register: zqsfp28_present_24_17_status */ + SYSFS_ATTR_PTR(port24_rst), /* register: zqsfp28_rst_24_17 */ + SYSFS_ATTR_PTR(port24_modsel), /* register: zqsfp28_modsel_24_17 */ + SYSFS_ATTR_PTR(port24_lpmode), /* register: zqsfp28_lpmode_24_17 */ + SYSFS_ATTR_PTR(port24_irq_status), /* register: zqsfp28_irq_24_17_status */ + SYSFS_ATTR_PTR(port24_irq_msk), /* register: zqsfp28_irq_msk_24_17_status */ + NULL +}; + +static struct attribute *port25_attributes[] = { + SYSFS_ATTR_PTR(port25_present), /* register: zqsfp28_present_32_25_status */ + SYSFS_ATTR_PTR(port25_rst), /* register: zqsfp28_rst_32_25 */ + SYSFS_ATTR_PTR(port25_modsel), /* register: zqsfp28_modsel_32_25 */ + SYSFS_ATTR_PTR(port25_lpmode), /* register: zqsfp28_lpmode_32_25 */ + SYSFS_ATTR_PTR(port25_irq_status), /* register: zqsfp28_irq_32_25_status */ + SYSFS_ATTR_PTR(port25_irq_msk), /* register: zqsfp28_irq_msk_32_25_status */ + NULL +}; + +static struct attribute *port26_attributes[] = { + SYSFS_ATTR_PTR(port26_present), /* register: zqsfp28_present_32_25_status */ + SYSFS_ATTR_PTR(port26_rst), /* register: zqsfp28_rst_32_25 */ + SYSFS_ATTR_PTR(port26_modsel), /* register: zqsfp28_modsel_32_25 */ + SYSFS_ATTR_PTR(port26_lpmode), /* register: zqsfp28_lpmode_32_25 */ + SYSFS_ATTR_PTR(port26_irq_status), /* register: zqsfp28_irq_32_25_status */ + SYSFS_ATTR_PTR(port26_irq_msk), /* register: zqsfp28_irq_msk_32_25_status */ + NULL +}; + +static struct attribute *port27_attributes[] = { + SYSFS_ATTR_PTR(port27_present), /* register: zqsfp28_present_32_25_status */ + SYSFS_ATTR_PTR(port27_rst), /* register: zqsfp28_rst_32_25 */ + SYSFS_ATTR_PTR(port27_modsel), /* register: zqsfp28_modsel_32_25 */ + SYSFS_ATTR_PTR(port27_lpmode), /* register: zqsfp28_lpmode_32_25 */ + SYSFS_ATTR_PTR(port27_irq_status), /* register: zqsfp28_irq_32_25_status */ + SYSFS_ATTR_PTR(port27_irq_msk), /* register: zqsfp28_irq_msk_32_25_status */ + NULL +}; + +static struct attribute *port28_attributes[] = { + SYSFS_ATTR_PTR(port28_present), /* register: zqsfp28_present_32_25_status */ + SYSFS_ATTR_PTR(port28_rst), /* register: zqsfp28_rst_32_25 */ + SYSFS_ATTR_PTR(port28_modsel), /* register: zqsfp28_modsel_32_25 */ + SYSFS_ATTR_PTR(port28_lpmode), /* register: zqsfp28_lpmode_32_25 */ + SYSFS_ATTR_PTR(port28_irq_status), /* register: zqsfp28_irq_32_25_status */ + SYSFS_ATTR_PTR(port28_irq_msk), /* register: zqsfp28_irq_msk_32_25_status */ + NULL +}; + +static struct attribute *port29_attributes[] = { + SYSFS_ATTR_PTR(port29_present), /* register: zqsfp28_present_32_25_status */ + SYSFS_ATTR_PTR(port29_rst), /* register: zqsfp28_rst_32_25 */ + SYSFS_ATTR_PTR(port29_modsel), /* register: zqsfp28_modsel_32_25 */ + SYSFS_ATTR_PTR(port29_lpmode), /* register: zqsfp28_lpmode_32_25 */ + SYSFS_ATTR_PTR(port29_irq_status), /* register: zqsfp28_irq_32_25_status */ + SYSFS_ATTR_PTR(port29_irq_msk), /* register: zqsfp28_irq_msk_32_25_status */ + NULL +}; + +static struct attribute *port30_attributes[] = { + SYSFS_ATTR_PTR(port30_present), /* register: zqsfp28_present_32_25_status */ + SYSFS_ATTR_PTR(port30_rst), /* register: zqsfp28_rst_32_25 */ + SYSFS_ATTR_PTR(port30_modsel), /* register: zqsfp28_modsel_32_25 */ + SYSFS_ATTR_PTR(port30_lpmode), /* register: zqsfp28_lpmode_32_25 */ + SYSFS_ATTR_PTR(port30_irq_status), /* register: zqsfp28_irq_32_25_status */ + SYSFS_ATTR_PTR(port30_irq_msk), /* register: zqsfp28_irq_msk_32_25_status */ + NULL +}; + +static struct attribute *port31_attributes[] = { + SYSFS_ATTR_PTR(port31_present), /* register: zqsfp28_present_32_25_status */ + SYSFS_ATTR_PTR(port31_rst), /* register: zqsfp28_rst_32_25 */ + SYSFS_ATTR_PTR(port31_modsel), /* register: zqsfp28_modsel_32_25 */ + SYSFS_ATTR_PTR(port31_lpmode), /* register: zqsfp28_lpmode_32_25 */ + SYSFS_ATTR_PTR(port31_irq_status), /* register: zqsfp28_irq_32_25_status */ + SYSFS_ATTR_PTR(port31_irq_msk), /* register: zqsfp28_irq_msk_32_25_status */ + NULL +}; + +static struct attribute *port32_attributes[] = { + SYSFS_ATTR_PTR(port32_present), /* register: zqsfp28_present_32_25_status */ + SYSFS_ATTR_PTR(port32_rst), /* register: zqsfp28_rst_32_25 */ + SYSFS_ATTR_PTR(port32_modsel), /* register: zqsfp28_modsel_32_25 */ + SYSFS_ATTR_PTR(port32_lpmode), /* register: zqsfp28_lpmode_32_25 */ + SYSFS_ATTR_PTR(port32_irq_status), /* register: zqsfp28_irq_32_25_status */ + SYSFS_ATTR_PTR(port32_irq_msk), /* register: zqsfp28_irq_msk_32_25_status */ + NULL +}; + +static const struct attribute_group slave_cpld_port_group[] = { + {.attrs = port17_attributes, + .name = "port17",}, + {.attrs = port18_attributes, + .name = "port18",}, + {.attrs = port19_attributes, + .name = "port19",}, + {.attrs = port20_attributes, + .name = "port20",}, + {.attrs = port21_attributes, + .name = "port21",}, + {.attrs = port22_attributes, + .name = "port22",}, + {.attrs = port23_attributes, + .name = "port23",}, + {.attrs = port24_attributes, + .name = "port24",}, + {.attrs = port25_attributes, + .name = "port25",}, + {.attrs = port26_attributes, + .name = "port26",}, + {.attrs = port27_attributes, + .name = "port27",}, + {.attrs = port28_attributes, + .name = "port28",}, + {.attrs = port29_attributes, + .name = "port29",}, + {.attrs = port30_attributes, + .name = "port30",}, + {.attrs = port31_attributes, + .name = "port31",}, + {.attrs = port32_attributes, + .name = "port32",} +}; + +/* ----------------define misc group---------------------------- */ +static struct attribute *misc_attributes[] = { + SYSFS_ATTR_PTR(mjr_rev), /* register: slv_cpld_rev */ + SYSFS_ATTR_PTR(mnr_rev), /* register: slv_cpld_rev */ + SYSFS_ATTR_PTR(scrtch_reg), /* register: slv_cpld_gpr */ + SYSFS_ATTR_PTR(brd_rev), /* register: mb_brd_rev_type */ + SYSFS_ATTR_PTR(brd_type), /* register: mb_brd_rev_type */ + + SYSFS_ATTR_PTR(port_17_24_present), /* register: zqsfp28_present_24_17_status */ + SYSFS_ATTR_PTR(port_25_32_present), /* register: zqsfp28_present_32_25_status */ + SYSFS_ATTR_PTR(port_17_24_rst), /* register: zqsfp28_rst_24_17 */ + SYSFS_ATTR_PTR(port_25_32_rst), /* register: zqsfp28_rst_32_25 */ + SYSFS_ATTR_PTR(port_17_24_modsel), /* register: zqsfp28_modsel_24_17 */ + SYSFS_ATTR_PTR(port_25_32_modsel), /* register: zqsfp28_modsel_32_25 */ + SYSFS_ATTR_PTR(port_17_24_irq_status),/* register: zqsfp28_irq_24_17_status */ + SYSFS_ATTR_PTR(port_25_32_irq_status),/* register: zqsfp28_irq_32_25_status */ + SYSFS_ATTR_PTR(port_17_24_irq_msk), /* register: zqsfp28_irq_msk_24_17_status */ + SYSFS_ATTR_PTR(port_25_32_irq_msk), /* register: zqsfp28_irq_msk_32_25_status */ + SYSFS_ATTR_PTR(port_17_24_lpmode), /* register: zqsfp28_lpmode_24_17 */ + SYSFS_ATTR_PTR(port_25_32_lpmode), /* register: zqsfp28_lpmode_32_25 */ + + SYSFS_ATTR_PTR(debug), /* debug flag for print more messages */ + NULL +}; +static const struct attribute_group slave_cpld_group_misc = { + .attrs = misc_attributes, +}; + +static int slave_cpld_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct slave_cpld_data *slave_cpld; + int err, i; + int grp_number = (int)( sizeof(slave_cpld_port_group) / sizeof(slave_cpld_port_group[0])); + + /* allocate memory to slave_cpld */ + slave_cpld = devm_kzalloc(&client->dev, sizeof(struct slave_cpld_data) , GFP_KERNEL); + + if (!slave_cpld) + return -ENOMEM; + + mutex_init(&slave_cpld->lock); + + for(i = 0 ; i < grp_number ; i++){ + err = sysfs_create_group(&client->dev.kobj, &slave_cpld_port_group[i]); + if (err){ + printk("%s: Error creeat port group %d.\n", __FUNCTION__, i+1); + } + } + err = sysfs_create_group(&client->dev.kobj, &slave_cpld_group_misc); + if (err){ + printk("%s: Error creeat misc group.\n", __FUNCTION__); + } + + slave_cpld->client = client; + i2c_set_clientdata(client, slave_cpld); + + printk(KERN_INFO "%s: Slave CPLD LCMXO3LF created.\n", __FUNCTION__); + + return 0; + +} + +static int slave_cpld_remove(struct i2c_client *client) +{ + int i; + int grp_number = (int)( sizeof(slave_cpld_port_group) / sizeof(slave_cpld_port_group[0])); + + for(i = 0 ; i < grp_number ; i++){ + sysfs_remove_group(&client->dev.kobj, &slave_cpld_port_group[i]); + } + sysfs_remove_group(&client->dev.kobj, &slave_cpld_group_misc); + + printk(KERN_INFO "%s: Slave CPLD removed.\n", __FUNCTION__); + return 0; +} + +static struct i2c_driver slave_cpld_driver = { + .driver = { + .name = "slave_cpld", + .owner = THIS_MODULE, + }, + .probe = slave_cpld_probe, + .remove = slave_cpld_remove, + .id_table = slave_cpld_ids, +}; + +static int __init slave_cpld_init(void) +{ + printk(KERN_INFO "%s: init.\n", __FUNCTION__); + return i2c_add_driver(&slave_cpld_driver); +} +module_init(slave_cpld_init); + +static void __exit slave_cpld_exit(void) +{ + printk(KERN_INFO "%s: exit.\n", __FUNCTION__); + i2c_del_driver(&slave_cpld_driver); +} +module_exit(slave_cpld_exit); + +MODULE_DESCRIPTION("mitac_ly1200_32x_slave_cpld driver"); +MODULE_AUTHOR("Eddy Weng "); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_system_cpld.c b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_system_cpld.c new file mode 100644 index 000000000000..e5af8b70bf9a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/mitac_ly1200_32x_system_cpld.c @@ -0,0 +1,541 @@ +#define CONFIG_DRV_SYSCPLD_WDT 1 + +#include +#include + +#include "system_cpld_reg.h" +#include "system_cpld_sysfs.h" +#ifdef CONFIG_DRV_SYSCPLD_WDT +#include +#include +#include +#include +#include +#include +#endif + +static int debug_flag = 0; + +struct system_cpld_data { + struct mutex lock; + + struct i2c_client *client; + struct device_attribute bin; +}; +struct system_cpld_data *system_cpld; + +static const struct i2c_device_id system_cpld_ids[] = { + { "system_cpld", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, system_cpld_ids); + +static int system_cpld_raw_read(struct device *dev, struct device_attribute *attr, char *buf, + int reg_offset, int reg_width, int fld_shift, int fld_width, int fld_mask, char* reg_name){ + unsigned int reg_val = 0, fld_val; + static int debug_flag; + struct system_cpld_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int err; + + if (reg_width != 8){ + printk("%s: Register table width setting failed.\n", reg_name); + return -EINVAL; + } + mutex_lock(&data->lock); + if ((err = i2c_smbus_read_byte_data(client, (u8)reg_offset)) < 0) { + /* CPLD read error condition */; + mutex_unlock(&data->lock); + printk("%s: i2c read failed, error code = %d.\n", reg_name, err); + return err; + } + reg_val = err; + if (debug_flag) { + printk("%s: reg_offset = %d, width = %d, cur value = 0x%x.\n", reg_name, reg_offset, reg_width, reg_val); + } + mutex_unlock(&data->lock); + if (fld_width == reg_width) {fld_val = reg_val & fld_mask;} + else {fld_val = (reg_val >> fld_shift) & fld_mask;} + return sprintf(buf, "0x%x\n", fld_val); +} + +static int system_cpld_raw_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count, + int reg_offset, int reg_width, int fld_shift, int fld_width, int fld_mask, char* reg_name){ + int ret_code; + unsigned int reg_val, fld_val; + unsigned long val; + static int debug_flag; + struct system_cpld_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + if (reg_width != 8){ + printk("%s: Register table width setting failed.\n", reg_name); + return -EINVAL; + } + /* Parse buf and store to fld_val */ + if ((ret_code = kstrtoul(buf, 16, &val))){ + printk("%s: Conversion value = %s failed, errno = %d.\n", reg_name, buf, ret_code); + return ret_code; + } + fld_val = (unsigned int)val; + mutex_lock(&data->lock); + if ((ret_code = i2c_smbus_read_byte_data(client, (u8)reg_offset)) < 0) { + /* Handle CPLD read error condition */; + mutex_unlock(&data->lock); + printk("%s: i2c read failed, error code = %d.\n", reg_name, ret_code); + return ret_code; + } + reg_val = ret_code; + if (debug_flag) { + printk("%s: offset = %d, width = %d, cur value = 0x%x.\n", reg_name, reg_offset, reg_width, reg_val); + } + if (fld_width == reg_width) {reg_val = fld_val & fld_mask;} + else {reg_val = (reg_val & ~(fld_mask << fld_shift)) | + ((fld_val & (fld_mask)) << fld_shift);} + if ((ret_code = i2c_smbus_write_byte_data(client, (u8)reg_offset, (u8)reg_val)) != 0) { + /* Handle CPLD write error condition */; + mutex_unlock(&data->lock); + printk("%s: i2c write failed, error code = %d.\n", reg_name, ret_code); + return ret_code; + } + else if (debug_flag) { + printk("%s: offset = %d, width = %d, new value = 0x%x.\n", reg_name, reg_offset, reg_width, reg_val); + } + mutex_unlock(&data->lock); + return count; +} + +/*--------------------special file for debug---------------------- */ +static ssize_t system_cpld_debug_read(struct device *dev, struct device_attribute *attr, + char *buf){ + + return sprintf(buf, "%d\n", debug_flag); +} + + +static ssize_t system_cpld_debug_write(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int temp; + int error; + + error = kstrtoint(buf, 10, &temp); + if (error){ + printk(KERN_INFO "%s: Conversion value = %s failed.\n", __FUNCTION__, buf); + return count; + } + debug_flag = temp; + + if(debug_flag) + printk("%s, debug_flag = %d\n", __FUNCTION__, debug_flag); + + return count; +} +SYSFS_MISC_RW_ATTR_DEF(debug, system_cpld_debug_read, system_cpld_debug_write) + + +/* ----------------define misc group---------------------------- */ +static struct attribute *misc_attributes[] = { + SYSFS_ATTR_PTR(mjr_rev), /* register: sys_cpld_rev */ + SYSFS_ATTR_PTR(mnr_rev), /* register: sys_cpld_rev */ + + SYSFS_ATTR_PTR(scrtch_reg), /* register: sys_cpld_gpr */ + + SYSFS_ATTR_PTR(brd_rev), /* register: cpu_brd_rev_type */ + SYSFS_ATTR_PTR(brd_type), /* register: cpu_brd_rev_type */ + + SYSFS_ATTR_PTR(ssd_present), /* register: sys_srr */ + SYSFS_ATTR_PTR(spi_cs_sel), /* register: sys_srr */ + SYSFS_ATTR_PTR(rst_bios_switch), /* register: sys_srr */ + SYSFS_ATTR_PTR(cpld_upgrade_rst), /* register: sys_srr */ + + SYSFS_ATTR_PTR(cpld_spi_wp), /* register: sys_eeprom_wp */ + SYSFS_ATTR_PTR(system_id_eeprom_wp), /* register: sys_eeprom_wp */ + SYSFS_ATTR_PTR(spi_me_wp), /* register: sys_eeprom_wp */ + SYSFS_ATTR_PTR(spi_bios_wp), /* register: sys_eeprom_wp */ + SYSFS_ATTR_PTR(spi_bak_bios_wp), /* register: sys_eeprom_wp */ + + SYSFS_ATTR_PTR(vrhot_irq_en), /* register: sys_irq */ + SYSFS_ATTR_PTR(cpu_thermtrip_irq_en), /* register: sys_irq */ + SYSFS_ATTR_PTR(temp_alert_irq_en), /* register: sys_irq */ + SYSFS_ATTR_PTR(vrhot_irq), /* register: sys_irq */ + SYSFS_ATTR_PTR(cpu_thermtrip_irq), /* register: sys_irq */ + SYSFS_ATTR_PTR(temp_alert_irq), /* register: sys_irq */ + + SYSFS_ATTR_PTR(wd_timer), /* register: sys_wd */ + SYSFS_ATTR_PTR(wd_en), /* register: sys_wd */ + SYSFS_ATTR_PTR(wd_punch), /* register: sys_wd */ + + SYSFS_ATTR_PTR(mb_rst_en), /* register: sys_mb_rst_en */ + + SYSFS_ATTR_PTR(pwr_v3p3_en), /* register: cpu_pwr_en_status */ + SYSFS_ATTR_PTR(pwr_vcc_vnn_en), /* register: cpu_pwr_en_status */ + SYSFS_ATTR_PTR(pwr_vccsram_en), /* register: cpu_pwr_en_status */ + SYSFS_ATTR_PTR(pwr_vddq_en), /* register: cpu_pwr_en_status */ + SYSFS_ATTR_PTR(pwr_vcc_ref_en), /* register: cpu_pwr_en_status */ + SYSFS_ATTR_PTR(pwr_v1p05_en), /* register: cpu_pwr_en_status */ + SYSFS_ATTR_PTR(pwr_v1p8_en), /* register: cpu_pwr_en_status */ + SYSFS_ATTR_PTR(pwr_v2p5_en), /* register: cpu_pwr_en_status */ + + SYSFS_ATTR_PTR(pg_v3p3), /* register: cpu_pwr_status */ + SYSFS_ATTR_PTR(pg_vcc_vnn), /* register: cpu_pwr_status */ + SYSFS_ATTR_PTR(pg_vccsram), /* register: cpu_pwr_status */ + SYSFS_ATTR_PTR(pg_vddq), /* register: cpu_pwr_status */ + SYSFS_ATTR_PTR(pg_vcc_ref), /* register: cpu_pwr_status */ + SYSFS_ATTR_PTR(pg_v1p05), /* register: cpu_pwr_status */ + SYSFS_ATTR_PTR(pg_v1p8), /* register: cpu_pwr_status */ + SYSFS_ATTR_PTR(pg_v2p5), /* register: cpu_pwr_status */ + + SYSFS_ATTR_PTR(debug), /* debug flag for print more messages */ + NULL +}; +static const struct attribute_group system_cpld_group_misc = { + .attrs = misc_attributes, +}; + +#ifdef CONFIG_DRV_SYSCPLD_WDT +/* + ***************************************************************************** + * + * Watchdog Driver + * + ***************************************************************************** + */ +/* wdt_timeout[] are defined by CPLD spec , -1 means researved + 300 sec is not supported */ +int wdt_timeout[]={15,30,60,90,120,180,240,300,-1,-1,-1,-1,-1,-1,-1,-1}; +#define WD_TIMO_MAX_NUM 16 +/* Default margin */ +#define WD_TIMO 30 + +static int wdt_margin = WD_TIMO; +module_param(wdt_margin, int, 0); +MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default " + __MODULE_STRING(WD_TIMO) "s)"); + +static unsigned long wdt_is_open; +static int boot_flag; + +/** + * wdt_ping: + * + * Reload counter one with the watchdog timeout. We don't bother reloading + * the cascade counter. + */ +static void wdt_ping(void) +{ + struct device *dev = &system_cpld->client->dev; + struct device_attribute *fake_attr=NULL; + char *buf="0";/* 0: punch is defined by CPLD spec */ + int err; + err = system_cpld_wd_punch_raw_write(dev, fake_attr, buf, (size_t)0); + if(err < 0){ + system_cpld_wd_punch_raw_write(dev, fake_attr, buf, (size_t)0); + } +} + +/** + * wdt_disable: + * + * disables watchdog. + */ +static void wdt_disable(void) +{ + struct device *dev = &system_cpld->client->dev; + struct device_attribute *fake_attr=NULL; + char *buf="0";/* 0: disable is defined by CPLD spec */ + int err; + err = system_cpld_wd_en_raw_write(dev, fake_attr, buf, (size_t)0); + if(err < 0){ + system_cpld_wd_en_raw_write(dev, fake_attr, buf, (size_t)0); + } +} + +/** + * wdt_enable: + * + * enables watchdog. + */ +static void wdt_enable(void) +{ + struct device *dev = &system_cpld->client->dev; + struct device_attribute *fake_attr=NULL; + char *buf="1";/* 1: enable is defined by CPLD spec */ + int err; + err = system_cpld_wd_en_raw_write(dev, fake_attr, buf, (size_t)0); + if(err < 0){ + system_cpld_wd_en_raw_write(dev, fake_attr, buf, (size_t)0); + } +} + +/** + * wdt_set_timeout: + * + * set watchdog timeout. + */ +static void wdt_set_timeout(int index) +{ + struct device *dev = &system_cpld->client->dev; + struct device_attribute *fake_attr=NULL; + char buf[1]; + if ( WD_TIMO_MAX_NUM == 16 ) { + sprintf(buf,"%x",index); + system_cpld_wd_timer_raw_write(dev, fake_attr, buf, (size_t)0); + } + else + printk(KERN_INFO "%s: It is out of spec.\n", __FUNCTION__); +} + +/** + * wdt_write: + * @file: file handle to the watchdog + * @buf: buffer to write (unused as data does not matter here + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we we don't define content meaning. + */ +static ssize_t wdt_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + if (count) { + wdt_ping(); + return 1; + } + return 0; +} + +/** + * wdt_ioctl: + * @inode: inode of the device + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. We only actually usefully support + * querying capabilities and current status. + */ +static int wdt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int new_margin, rv, i; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + .firmware_version = 1, + .identity = "SYS_CPLD WTD" + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info __user *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(boot_flag, (int __user *)arg); + case WDIOC_KEEPALIVE: + wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int __user *)arg)) + return -EFAULT; + /* Arbitrary, can't find the card's limits */ + if (new_margin <= 1) + return -EINVAL; + for( i=0; i= WD_TIMO_MAX_NUM || i < 0 ) + return -EINVAL; + wdt_set_timeout(i); + case WDIOC_GETTIMEOUT: + return put_user(wdt_margin, (int __user *)arg); + + case WDIOC_SETOPTIONS: + if (copy_from_user(&rv, (int __user *)arg, sizeof(int))) + return -EFAULT; + + if (rv & WDIOS_DISABLECARD) { + pr_info("System CPLD: disable watchdog\n"); + wdt_disable(); + } + + if (rv & WDIOS_ENABLECARD) { + pr_info("System CPLD: enable watchdog\n"); + wdt_enable(); + } + return -EINVAL; + } + return -ENOTTY; +} + +static long wdt_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + + ret = wdt_ioctl(file, cmd, arg); + + return ret; +} + +/** + * wdt_open: + * @inode: inode of device + * @file: file handle to device + * + */ +static int wdt_open(struct inode *inode, struct file *file) +{ + if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { + if (test_and_set_bit(0, &wdt_is_open)) { + return -EBUSY; + } + /* + * Activate + */ + + wdt_enable(); + return nonseekable_open(inode, file); + } + return -ENODEV; +} + +/** + * wdt_close: + * @inode: inode to board + * @file: file handle to board + * + */ +static int wdt_release(struct inode *inode, struct file *file) +{ + if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) + clear_bit(0, &wdt_is_open); + return 0; +} + +/** + * notify_sys: + * @this: our notifier block + * @code: the event being reported + * @unused: unused + * + * Our notifier is called on system shutdowns. We want to turn the card + * off at reboot otherwise the machine will reboot again during memory + * test or worse yet during the following fsck. This would suck, in fact + * trust me - if it happens it does suck. + */ +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + /* Disable Watchdog */ + wdt_disable(); + return NOTIFY_DONE; +} + +static const struct file_operations wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wdt_write, + .unlocked_ioctl = wdt_unlocked_ioctl, + .open = wdt_open, + .release = wdt_release, +}; + +static struct miscdevice wdt_dev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt_fops, +}; + +/* + * The WDT card needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ +static struct notifier_block wdt_notifier = { + .notifier_call = wdt_notify_sys, +}; +#endif /* CONFIG_DRV_SYSCPLD_WDT */ + +static int system_cpld_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int err; + + /* allocate memory to system_cpld */ + system_cpld = devm_kzalloc(&client->dev, sizeof(struct system_cpld_data) , GFP_KERNEL); + + if (!system_cpld) + return -ENOMEM; + + mutex_init(&system_cpld->lock); + + err = sysfs_create_group(&client->dev.kobj, &system_cpld_group_misc); + if (err){ + printk("%s: Error creeat misc group.\n", __FUNCTION__); + } + + system_cpld->client = client; + i2c_set_clientdata(client, system_cpld); + + printk(KERN_INFO "%s: System CPLD LCMXO3LF created.\n", __FUNCTION__); + +#ifdef CONFIG_DRV_SYSCPLD_WDT + err = misc_register(&wdt_dev); + if (err) + return err; + err = register_reboot_notifier(&wdt_notifier); + if (err) { + misc_deregister(&wdt_dev); + return err; + } + printk(KERN_INFO "%s: System CPLD watchdog created.\n", __FUNCTION__); +#endif + + return 0; + +} + +static int system_cpld_remove(struct i2c_client *client) +{ +#ifdef CONFIG_DRV_SYSCPLD_WDT + misc_deregister(&wdt_dev); + unregister_reboot_notifier(&wdt_notifier); +#endif + sysfs_remove_group(&client->dev.kobj, &system_cpld_group_misc); + + printk(KERN_INFO "%s: System CPLD removed.\n", __FUNCTION__); + return 0; +} + +static struct i2c_driver system_cpld_driver = { + .driver = { + .name = "system_cpld", + .owner = THIS_MODULE, + }, + .probe = system_cpld_probe, + .remove = system_cpld_remove, + .id_table = system_cpld_ids, +}; + +static int __init system_cpld_init(void) +{ + printk(KERN_INFO "%s: init.\n", __FUNCTION__); + return i2c_add_driver(&system_cpld_driver); +} +module_init(system_cpld_init); + +static void __exit system_cpld_exit(void) +{ + printk(KERN_INFO "%s: exit.\n", __FUNCTION__); + i2c_del_driver(&system_cpld_driver); +} +module_exit(system_cpld_exit); + +MODULE_DESCRIPTION("mitac_ly1200_32x_system_cpld driver"); +MODULE_AUTHOR("Eddy Weng "); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/pmbus.h b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/pmbus.h new file mode 100644 index 000000000000..55cb8663e6e9 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/pmbus.h @@ -0,0 +1,366 @@ +#ifndef PMBUS_H +#define PMBUS_H + +/* + * Registers + */ +#define PMBUS_PAGE 0x00 +#define PMBUS_OPERATION 0x01 +#define PMBUS_ON_OFF_CONFIG 0x02 +#define PMBUS_CLEAR_FAULTS 0x03 +#define PMBUS_PHASE 0x04 + +#define PMBUS_CAPABILITY 0x19 +#define PMBUS_QUERY 0x1A + +#define PMBUS_VOUT_MODE 0x20 +#define PMBUS_VOUT_COMMAND 0x21 +#define PMBUS_VOUT_TRIM 0x22 +#define PMBUS_VOUT_CAL_OFFSET 0x23 +#define PMBUS_VOUT_MAX 0x24 +#define PMBUS_VOUT_MARGIN_HIGH 0x25 +#define PMBUS_VOUT_MARGIN_LOW 0x26 +#define PMBUS_VOUT_TRANSITION_RATE 0x27 +#define PMBUS_VOUT_DROOP 0x28 +#define PMBUS_VOUT_SCALE_LOOP 0x29 +#define PMBUS_VOUT_SCALE_MONITOR 0x2A + +#define PMBUS_COEFFICIENTS 0x30 +#define PMBUS_POUT_MAX 0x31 + +#define PMBUS_FAN_CONFIG_12 0x3A +#define PMBUS_FAN_COMMAND_1 0x3B +#define PMBUS_FAN_COMMAND_2 0x3C +#define PMBUS_FAN_CONFIG_34 0x3D +#define PMBUS_FAN_COMMAND_3 0x3E +#define PMBUS_FAN_COMMAND_4 0x3F + +#define PMBUS_VOUT_OV_FAULT_LIMIT 0x40 +#define PMBUS_VOUT_OV_FAULT_RESPONSE 0x41 +#define PMBUS_VOUT_OV_WARN_LIMIT 0x42 +#define PMBUS_VOUT_UV_WARN_LIMIT 0x43 +#define PMBUS_VOUT_UV_FAULT_LIMIT 0x44 +#define PMBUS_VOUT_UV_FAULT_RESPONSE 0x45 +#define PMBUS_IOUT_OC_FAULT_LIMIT 0x46 +#define PMBUS_IOUT_OC_FAULT_RESPONSE 0x47 +#define PMBUS_IOUT_OC_LV_FAULT_LIMIT 0x48 +#define PMBUS_IOUT_OC_LV_FAULT_RESPONSE 0x49 +#define PMBUS_IOUT_OC_WARN_LIMIT 0x4A +#define PMBUS_IOUT_UC_FAULT_LIMIT 0x4B +#define PMBUS_IOUT_UC_FAULT_RESPONSE 0x4C + +#define PMBUS_OT_FAULT_LIMIT 0x4F +#define PMBUS_OT_FAULT_RESPONSE 0x50 +#define PMBUS_OT_WARN_LIMIT 0x51 +#define PMBUS_UT_WARN_LIMIT 0x52 +#define PMBUS_UT_FAULT_LIMIT 0x53 +#define PMBUS_UT_FAULT_RESPONSE 0x54 +#define PMBUS_VIN_OV_FAULT_LIMIT 0x55 +#define PMBUS_VIN_OV_FAULT_RESPONSE 0x56 +#define PMBUS_VIN_OV_WARN_LIMIT 0x57 +#define PMBUS_VIN_UV_WARN_LIMIT 0x58 +#define PMBUS_VIN_UV_FAULT_LIMIT 0x59 + +#define PMBUS_IIN_OC_FAULT_LIMIT 0x5B +#define PMBUS_IIN_OC_WARN_LIMIT 0x5D + +#define PMBUS_POUT_OP_FAULT_LIMIT 0x68 +#define PMBUS_POUT_OP_WARN_LIMIT 0x6A +#define PMBUS_PIN_OP_WARN_LIMIT 0x6B + +#define PMBUS_STATUS_BYTE 0x78 +#define PMBUS_STATUS_WORD 0x79 +#define PMBUS_STATUS_VOUT 0x7A +#define PMBUS_STATUS_IOUT 0x7B +#define PMBUS_STATUS_INPUT 0x7C +#define PMBUS_STATUS_TEMPERATURE 0x7D +#define PMBUS_STATUS_CML 0x7E +#define PMBUS_STATUS_OTHER 0x7F +#define PMBUS_STATUS_MFR_SPECIFIC 0x80 +#define PMBUS_STATUS_FAN_12 0x81 +#define PMBUS_STATUS_FAN_34 0x82 + +#define PMBUS_READ_VIN 0x88 +#define PMBUS_READ_IIN 0x89 +#define PMBUS_READ_VCAP 0x8A +#define PMBUS_READ_VOUT 0x8B +#define PMBUS_READ_IOUT 0x8C +#define PMBUS_READ_TEMPERATURE_1 0x8D +#define PMBUS_READ_TEMPERATURE_2 0x8E +#define PMBUS_READ_TEMPERATURE_3 0x8F +#define PMBUS_READ_FAN_SPEED_1 0x90 +#define PMBUS_READ_FAN_SPEED_2 0x91 +#define PMBUS_READ_FAN_SPEED_3 0x92 +#define PMBUS_READ_FAN_SPEED_4 0x93 +#define PMBUS_READ_DUTY_CYCLE 0x94 +#define PMBUS_READ_FREQUENCY 0x95 +#define PMBUS_READ_POUT 0x96 +#define PMBUS_READ_PIN 0x97 + +#define PMBUS_REVISION 0x98 +#define PMBUS_MFR_ID 0x99 +#define PMBUS_MFR_MODEL 0x9A +#define PMBUS_MFR_REVISION 0x9B +#define PMBUS_MFR_LOCATION 0x9C +#define PMBUS_MFR_DATE 0x9D +#define PMBUS_MFR_SERIAL 0x9E + +/* + * Virtual registers. + * Useful to support attributes which are not supported by standard PMBus + * registers but exist as manufacturer specific registers on individual chips. + * Must be mapped to real registers in device specific code. + * + * Semantics: + * Virtual registers are all word size. + * READ registers are read-only; writes are either ignored or return an error. + * RESET registers are read/write. Reading reset registers returns zero + * (used for detection), writing any value causes the associated history to be + * reset. + * Virtual registers have to be handled in device specific driver code. Chip + * driver code returns non-negative register values if a virtual register is + * supported, or a negative error code if not. The chip driver may return + * -ENODATA or any other error code in this case, though an error code other + * than -ENODATA is handled more efficiently and thus preferred. Either case, + * the calling PMBus core code will abort if the chip driver returns an error + * code when reading or writing virtual registers. + */ +#define PMBUS_VIRT_BASE 0x100 +#define PMBUS_VIRT_READ_TEMP_AVG (PMBUS_VIRT_BASE + 0) +#define PMBUS_VIRT_READ_TEMP_MIN (PMBUS_VIRT_BASE + 1) +#define PMBUS_VIRT_READ_TEMP_MAX (PMBUS_VIRT_BASE + 2) +#define PMBUS_VIRT_RESET_TEMP_HISTORY (PMBUS_VIRT_BASE + 3) +#define PMBUS_VIRT_READ_VIN_AVG (PMBUS_VIRT_BASE + 4) +#define PMBUS_VIRT_READ_VIN_MIN (PMBUS_VIRT_BASE + 5) +#define PMBUS_VIRT_READ_VIN_MAX (PMBUS_VIRT_BASE + 6) +#define PMBUS_VIRT_RESET_VIN_HISTORY (PMBUS_VIRT_BASE + 7) +#define PMBUS_VIRT_READ_IIN_AVG (PMBUS_VIRT_BASE + 8) +#define PMBUS_VIRT_READ_IIN_MIN (PMBUS_VIRT_BASE + 9) +#define PMBUS_VIRT_READ_IIN_MAX (PMBUS_VIRT_BASE + 10) +#define PMBUS_VIRT_RESET_IIN_HISTORY (PMBUS_VIRT_BASE + 11) +#define PMBUS_VIRT_READ_PIN_AVG (PMBUS_VIRT_BASE + 12) +#define PMBUS_VIRT_READ_PIN_MAX (PMBUS_VIRT_BASE + 13) +#define PMBUS_VIRT_RESET_PIN_HISTORY (PMBUS_VIRT_BASE + 14) +#define PMBUS_VIRT_READ_POUT_AVG (PMBUS_VIRT_BASE + 15) +#define PMBUS_VIRT_READ_POUT_MAX (PMBUS_VIRT_BASE + 16) +#define PMBUS_VIRT_RESET_POUT_HISTORY (PMBUS_VIRT_BASE + 17) +#define PMBUS_VIRT_READ_VOUT_AVG (PMBUS_VIRT_BASE + 18) +#define PMBUS_VIRT_READ_VOUT_MIN (PMBUS_VIRT_BASE + 19) +#define PMBUS_VIRT_READ_VOUT_MAX (PMBUS_VIRT_BASE + 20) +#define PMBUS_VIRT_RESET_VOUT_HISTORY (PMBUS_VIRT_BASE + 21) +#define PMBUS_VIRT_READ_IOUT_AVG (PMBUS_VIRT_BASE + 22) +#define PMBUS_VIRT_READ_IOUT_MIN (PMBUS_VIRT_BASE + 23) +#define PMBUS_VIRT_READ_IOUT_MAX (PMBUS_VIRT_BASE + 24) +#define PMBUS_VIRT_RESET_IOUT_HISTORY (PMBUS_VIRT_BASE + 25) +#define PMBUS_VIRT_READ_TEMP2_AVG (PMBUS_VIRT_BASE + 26) +#define PMBUS_VIRT_READ_TEMP2_MIN (PMBUS_VIRT_BASE + 27) +#define PMBUS_VIRT_READ_TEMP2_MAX (PMBUS_VIRT_BASE + 28) +#define PMBUS_VIRT_RESET_TEMP2_HISTORY (PMBUS_VIRT_BASE + 29) + +#define PMBUS_VIRT_READ_VMON (PMBUS_VIRT_BASE + 30) +#define PMBUS_VIRT_VMON_UV_WARN_LIMIT (PMBUS_VIRT_BASE + 31) +#define PMBUS_VIRT_VMON_OV_WARN_LIMIT (PMBUS_VIRT_BASE + 32) +#define PMBUS_VIRT_VMON_UV_FAULT_LIMIT (PMBUS_VIRT_BASE + 33) +#define PMBUS_VIRT_VMON_OV_FAULT_LIMIT (PMBUS_VIRT_BASE + 34) +#define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35) + +/* + * CAPABILITY + */ +#define PB_CAPABILITY_SMBALERT (1<<4) +#define PB_CAPABILITY_ERROR_CHECK (1<<7) + +/* + * VOUT_MODE + */ +#define PB_VOUT_MODE_MODE_MASK 0xe0 +#define PB_VOUT_MODE_PARAM_MASK 0x1f + +#define PB_VOUT_MODE_LINEAR 0x00 +#define PB_VOUT_MODE_VID 0x20 +#define PB_VOUT_MODE_DIRECT 0x40 + +/* + * Fan configuration + */ +#define PB_FAN_2_PULSE_MASK ((1 << 0) | (1 << 1)) +#define PB_FAN_2_RPM (1 << 2) +#define PB_FAN_2_INSTALLED (1 << 3) +#define PB_FAN_1_PULSE_MASK ((1 << 4) | (1 << 5)) +#define PB_FAN_1_RPM (1 << 6) +#define PB_FAN_1_INSTALLED (1 << 7) + +/* + * STATUS_BYTE, STATUS_WORD (lower) + */ +#define PB_STATUS_NONE_ABOVE (1<<0) +#define PB_STATUS_CML (1<<1) +#define PB_STATUS_TEMPERATURE (1<<2) +#define PB_STATUS_VIN_UV (1<<3) +#define PB_STATUS_IOUT_OC (1<<4) +#define PB_STATUS_VOUT_OV (1<<5) +#define PB_STATUS_OFF (1<<6) +#define PB_STATUS_BUSY (1<<7) + +/* + * STATUS_WORD (upper) + */ +#define PB_STATUS_UNKNOWN (1<<8) +#define PB_STATUS_OTHER (1<<9) +#define PB_STATUS_FANS (1<<10) +#define PB_STATUS_POWER_GOOD_N (1<<11) +#define PB_STATUS_WORD_MFR (1<<12) +#define PB_STATUS_INPUT (1<<13) +#define PB_STATUS_IOUT_POUT (1<<14) +#define PB_STATUS_VOUT (1<<15) + +/* + * STATUS_IOUT + */ +#define PB_POUT_OP_WARNING (1<<0) +#define PB_POUT_OP_FAULT (1<<1) +#define PB_POWER_LIMITING (1<<2) +#define PB_CURRENT_SHARE_FAULT (1<<3) +#define PB_IOUT_UC_FAULT (1<<4) +#define PB_IOUT_OC_WARNING (1<<5) +#define PB_IOUT_OC_LV_FAULT (1<<6) +#define PB_IOUT_OC_FAULT (1<<7) + +/* + * STATUS_VOUT, STATUS_INPUT + */ +#define PB_VOLTAGE_UV_FAULT (1<<4) +#define PB_VOLTAGE_UV_WARNING (1<<5) +#define PB_VOLTAGE_OV_WARNING (1<<6) +#define PB_VOLTAGE_OV_FAULT (1<<7) + +/* + * STATUS_INPUT + */ +#define PB_PIN_OP_WARNING (1<<0) +#define PB_IIN_OC_WARNING (1<<1) +#define PB_IIN_OC_FAULT (1<<2) + +/* + * STATUS_TEMPERATURE + */ +#define PB_TEMP_UT_FAULT (1<<4) +#define PB_TEMP_UT_WARNING (1<<5) +#define PB_TEMP_OT_WARNING (1<<6) +#define PB_TEMP_OT_FAULT (1<<7) + +/* + * STATUS_FAN + */ +#define PB_FAN_AIRFLOW_WARNING (1<<0) +#define PB_FAN_AIRFLOW_FAULT (1<<1) +#define PB_FAN_FAN2_SPEED_OVERRIDE (1<<2) +#define PB_FAN_FAN1_SPEED_OVERRIDE (1<<3) +#define PB_FAN_FAN2_WARNING (1<<4) +#define PB_FAN_FAN1_WARNING (1<<5) +#define PB_FAN_FAN2_FAULT (1<<6) +#define PB_FAN_FAN1_FAULT (1<<7) + +/* + * CML_FAULT_STATUS + */ +#define PB_CML_FAULT_OTHER_MEM_LOGIC (1<<0) +#define PB_CML_FAULT_OTHER_COMM (1<<1) +#define PB_CML_FAULT_PROCESSOR (1<<3) +#define PB_CML_FAULT_MEMORY (1<<4) +#define PB_CML_FAULT_PACKET_ERROR (1<<5) +#define PB_CML_FAULT_INVALID_DATA (1<<6) +#define PB_CML_FAULT_INVALID_COMMAND (1<<7) + +enum pmbus_sensor_classes { + PSC_VOLTAGE_IN = 0, + PSC_VOLTAGE_OUT, + PSC_CURRENT_IN, + PSC_CURRENT_OUT, + PSC_POWER, + PSC_TEMPERATURE, + PSC_FAN, + PSC_NUM_CLASSES /* Number of power sensor classes */ +}; + +#define PMBUS_PAGES 32 /* Per PMBus specification */ + +/* Functionality bit mask */ +#define PMBUS_HAVE_VIN (1 << 0) +#define PMBUS_HAVE_VCAP (1 << 1) +#define PMBUS_HAVE_VOUT (1 << 2) +#define PMBUS_HAVE_IIN (1 << 3) +#define PMBUS_HAVE_IOUT (1 << 4) +#define PMBUS_HAVE_PIN (1 << 5) +#define PMBUS_HAVE_POUT (1 << 6) +#define PMBUS_HAVE_FAN12 (1 << 7) +#define PMBUS_HAVE_FAN34 (1 << 8) +#define PMBUS_HAVE_TEMP (1 << 9) +#define PMBUS_HAVE_TEMP2 (1 << 10) +#define PMBUS_HAVE_TEMP3 (1 << 11) +#define PMBUS_HAVE_STATUS_VOUT (1 << 12) +#define PMBUS_HAVE_STATUS_IOUT (1 << 13) +#define PMBUS_HAVE_STATUS_INPUT (1 << 14) +#define PMBUS_HAVE_STATUS_TEMP (1 << 15) +#define PMBUS_HAVE_STATUS_FAN12 (1 << 16) +#define PMBUS_HAVE_STATUS_FAN34 (1 << 17) +#define PMBUS_HAVE_VMON (1 << 18) +#define PMBUS_HAVE_STATUS_VMON (1 << 19) + +enum pmbus_data_format { linear = 0, direct, vid }; + +struct pmbus_driver_info { + int pages; /* Total number of pages */ + enum pmbus_data_format format[PSC_NUM_CLASSES]; + /* + * Support one set of coefficients for each sensor type + * Used for chips providing data in direct mode. + */ + int m[PSC_NUM_CLASSES]; /* mantissa for direct data format */ + int b[PSC_NUM_CLASSES]; /* offset */ + int R[PSC_NUM_CLASSES]; /* exponent */ + + u32 func[PMBUS_PAGES]; /* Functionality, per page */ + /* + * The following functions map manufacturing specific register values + * to PMBus standard register values. Specify only if mapping is + * necessary. + * Functions return the register value (read) or zero (write) if + * successful. A return value of -ENODATA indicates that there is no + * manufacturer specific register, but that a standard PMBus register + * may exist. Any other negative return value indicates that the + * register does not exist, and that no attempt should be made to read + * the standard register. + */ + int (*read_byte_data)(struct i2c_client *client, int page, int reg); + int (*read_word_data)(struct i2c_client *client, int page, int reg); + int (*write_word_data)(struct i2c_client *client, int page, int reg, + u16 word); + int (*write_byte)(struct i2c_client *client, int page, u8 value); + /* + * The identify function determines supported PMBus functionality. + * This function is only necessary if a chip driver supports multiple + * chips, and the chip functionality is not pre-determined. + */ + int (*identify)(struct i2c_client *client, + struct pmbus_driver_info *info); +}; + +/* Function declarations */ + +void pmbus_clear_cache(struct i2c_client *client); +int pmbus_set_page(struct i2c_client *client, u8 page); +int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); +int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word); +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); +int pmbus_write_byte(struct i2c_client *client, int page, u8 value); +void pmbus_clear_faults(struct i2c_client *client); +bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); +bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); +int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, + struct pmbus_driver_info *info); +int pmbus_do_remove(struct i2c_client *client); +const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client + *client); + +#endif /* PMBUS_H */ diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/slave_cpld_reg.h b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/slave_cpld_reg.h new file mode 100644 index 000000000000..a8169bdb88ee --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/slave_cpld_reg.h @@ -0,0 +1,192 @@ +#ifndef __SLAVE_CPLD_REG +#define __SLAVE_CPLD_REG + +static int slave_cpld_raw_read(struct device *dev, struct device_attribute *attr, char *buf, + int reg_offset, int reg_width, int fld_shift, int fld_width, int fld_mask, char* reg_name); +static int slave_cpld_raw_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count, + int reg_offset, int reg_width, int fld_shift, int fld_width, int fld_mask, char* reg_name); + +/* generic CPLD read function */ +#define FLD_RAW_RD_FUNC(_reg, _fld, _wdh) static ssize_t \ +slave_cpld_##_fld##_raw_read(struct device *dev, struct device_attribute *attr, char *buf) { \ + return slave_cpld_raw_read(dev, attr, buf, _reg##_offset, _reg##_width, _fld##_shift, _fld##_width, _fld##_mask, #_reg); \ +} + +/* generic CPLD write function */ +#define FLD_RAW_WR_FUNC(_reg, _fld, _wdh) static ssize_t \ +slave_cpld_##_fld##_raw_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { \ + return slave_cpld_raw_write(dev, attr, buf, count, _reg##_offset, _reg##_width, _fld##_shift, _fld##_width, _fld##_mask, #_reg); \ +} + +/* CPLD register definition macros */ +#define REG_DEF(_reg, _off, _wdh) \ +static unsigned int _reg##_offset = (unsigned int)(_off); \ +static unsigned int _reg##_width = (unsigned int)(_wdh); + +/* CPLD register field definition macros, with generic read/write function */ +#define FLD_RAW_RO_DEF(_reg, _fld, _sft, _wdh) \ +static unsigned int _fld##_shift = (unsigned int)(_sft); \ +static unsigned int _fld##_width = (unsigned int)(_wdh); \ +static unsigned int _fld##_mask = ((((unsigned int)1) << (_wdh)) - 1); \ +FLD_RAW_RD_FUNC(_reg, _fld, _wdh) + +#define FLD_RAW_RW_DEF(_reg, _fld, _sft, _wdh) \ +static unsigned int _fld##_shift = (unsigned int)(_sft); \ +static unsigned int _fld##_width = (unsigned int)(_wdh); \ +static unsigned int _fld##_mask = ((((unsigned int)1) << (_wdh)) - 1); \ +FLD_RAW_RD_FUNC(_reg, _fld, _wdh) FLD_RAW_WR_FUNC(_reg, _fld, _wdh) + +/* declare slave CPLD registers */ +/* register name offset width */ +/* --------------------------------------- ------- ----- */ +REG_DEF( slv_cpld_rev, 0x00, 8) +REG_DEF( slv_cpld_gpr, 0x01, 8) +REG_DEF( mb_brd_rev_type, 0x02, 8) + +REG_DEF( zqsfp28_present_24_17_status, 0x10, 8) +REG_DEF( zqsfp28_present_32_25_status, 0x11, 8) +REG_DEF( zqsfp28_rst_24_17, 0x12, 8) +REG_DEF( zqsfp28_rst_32_25, 0x13, 8) +REG_DEF( zqsfp28_modsel_24_17, 0x14, 8) +REG_DEF( zqsfp28_modsel_32_25, 0x15, 8) +REG_DEF( zqsfp28_lpmode_24_17, 0x16, 8) +REG_DEF( zqsfp28_lpmode_32_25, 0x17, 8) +REG_DEF( zqsfp28_irq_24_17_status, 0x18, 8) +REG_DEF( zqsfp28_irq_32_25_status, 0x19, 8) +REG_DEF( zqsfp28_irq_msk_24_17_status, 0x1A, 8) +REG_DEF( zqsfp28_irq_msk_32_25_status, 0x1B, 8) + + +/* declare slave CPLD register's fields */ +/* register name field name shift width */ +/* ---------------------- ---------------- ------ ----- */ +FLD_RAW_RO_DEF( slv_cpld_rev, mjr_rev, 4, 4) +FLD_RAW_RO_DEF( slv_cpld_rev, mnr_rev, 0, 4) + +FLD_RAW_RW_DEF( slv_cpld_gpr, scrtch_reg, 0, 8) + +FLD_RAW_RO_DEF( mb_brd_rev_type, brd_rev, 4, 4) +FLD_RAW_RO_DEF( mb_brd_rev_type, brd_type, 0, 4) + +FLD_RAW_RO_DEF( zqsfp28_present_24_17_status, port24_present, 7, 1) +FLD_RAW_RO_DEF( zqsfp28_present_24_17_status, port23_present, 6, 1) +FLD_RAW_RO_DEF( zqsfp28_present_24_17_status, port22_present, 5, 1) +FLD_RAW_RO_DEF( zqsfp28_present_24_17_status, port21_present, 4, 1) +FLD_RAW_RO_DEF( zqsfp28_present_24_17_status, port20_present, 3, 1) +FLD_RAW_RO_DEF( zqsfp28_present_24_17_status, port19_present, 2, 1) +FLD_RAW_RO_DEF( zqsfp28_present_24_17_status, port18_present, 1, 1) +FLD_RAW_RO_DEF( zqsfp28_present_24_17_status, port17_present, 0, 1) + +FLD_RAW_RO_DEF( zqsfp28_present_32_25_status, port32_present, 7, 1) +FLD_RAW_RO_DEF( zqsfp28_present_32_25_status, port31_present, 6, 1) +FLD_RAW_RO_DEF( zqsfp28_present_32_25_status, port30_present, 5, 1) +FLD_RAW_RO_DEF( zqsfp28_present_32_25_status, port29_present, 4, 1) +FLD_RAW_RO_DEF( zqsfp28_present_32_25_status, port28_present, 3, 1) +FLD_RAW_RO_DEF( zqsfp28_present_32_25_status, port27_present, 2, 1) +FLD_RAW_RO_DEF( zqsfp28_present_32_25_status, port26_present, 1, 1) +FLD_RAW_RO_DEF( zqsfp28_present_32_25_status, port25_present, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_rst_24_17, port24_rst, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_24_17, port23_rst, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_24_17, port22_rst, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_24_17, port21_rst, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_24_17, port20_rst, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_24_17, port19_rst, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_24_17, port18_rst, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_24_17, port17_rst, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_rst_32_25, port32_rst, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_32_25, port31_rst, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_32_25, port30_rst, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_32_25, port29_rst, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_32_25, port28_rst, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_32_25, port27_rst, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_32_25, port26_rst, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_rst_32_25, port25_rst, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_modsel_24_17, port24_modsel, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_24_17, port23_modsel, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_24_17, port22_modsel, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_24_17, port21_modsel, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_24_17, port20_modsel, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_24_17, port19_modsel, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_24_17, port18_modsel, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_24_17, port17_modsel, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_modsel_32_25, port32_modsel, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_32_25, port31_modsel, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_32_25, port30_modsel, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_32_25, port29_modsel, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_32_25, port28_modsel, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_32_25, port27_modsel, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_32_25, port26_modsel, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_modsel_32_25, port25_modsel, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_lpmode_24_17, port24_lpmode, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_24_17, port23_lpmode, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_24_17, port22_lpmode, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_24_17, port21_lpmode, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_24_17, port20_lpmode, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_24_17, port19_lpmode, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_24_17, port18_lpmode, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_24_17, port17_lpmode, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_lpmode_32_25, port32_lpmode, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_32_25, port31_lpmode, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_32_25, port30_lpmode, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_32_25, port29_lpmode, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_32_25, port28_lpmode, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_32_25, port27_lpmode, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_32_25, port26_lpmode, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_lpmode_32_25, port25_lpmode, 0, 1) + +FLD_RAW_RO_DEF( zqsfp28_irq_24_17_status, port24_irq_status, 7, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_24_17_status, port23_irq_status, 6, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_24_17_status, port22_irq_status, 5, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_24_17_status, port21_irq_status, 4, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_24_17_status, port20_irq_status, 3, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_24_17_status, port19_irq_status, 2, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_24_17_status, port18_irq_status, 1, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_24_17_status, port17_irq_status, 0, 1) + +FLD_RAW_RO_DEF( zqsfp28_irq_32_25_status, port32_irq_status, 7, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_32_25_status, port31_irq_status, 6, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_32_25_status, port30_irq_status, 5, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_32_25_status, port29_irq_status, 4, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_32_25_status, port28_irq_status, 3, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_32_25_status, port27_irq_status, 2, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_32_25_status, port26_irq_status, 1, 1) +FLD_RAW_RO_DEF( zqsfp28_irq_32_25_status, port25_irq_status, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_irq_msk_24_17_status, port24_irq_msk, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_24_17_status, port23_irq_msk, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_24_17_status, port22_irq_msk, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_24_17_status, port21_irq_msk, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_24_17_status, port20_irq_msk, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_24_17_status, port19_irq_msk, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_24_17_status, port18_irq_msk, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_24_17_status, port17_irq_msk, 0, 1) + +FLD_RAW_RW_DEF( zqsfp28_irq_msk_32_25_status, port32_irq_msk, 7, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_32_25_status, port31_irq_msk, 6, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_32_25_status, port30_irq_msk, 5, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_32_25_status, port29_irq_msk, 4, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_32_25_status, port28_irq_msk, 3, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_32_25_status, port27_irq_msk, 2, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_32_25_status, port26_irq_msk, 1, 1) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_32_25_status, port25_irq_msk, 0, 1) + +FLD_RAW_RO_DEF( zqsfp28_present_24_17_status, port_17_24_present, 0, 8) +FLD_RAW_RO_DEF( zqsfp28_present_32_25_status, port_25_32_present, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_rst_24_17, port_17_24_rst, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_rst_32_25, port_25_32_rst, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_modsel_24_17, port_17_24_modsel, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_modsel_32_25, port_25_32_modsel, 0, 8) +FLD_RAW_RO_DEF( zqsfp28_irq_24_17_status, port_17_24_irq_status,0, 8) +FLD_RAW_RO_DEF( zqsfp28_irq_32_25_status, port_25_32_irq_status,0, 8) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_24_17_status, port_17_24_irq_msk, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_irq_msk_32_25_status, port_25_32_irq_msk, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_lpmode_24_17, port_17_24_lpmode, 0, 8) +FLD_RAW_RW_DEF( zqsfp28_lpmode_32_25, port_25_32_lpmode, 0, 8) + +#endif /* __SLAVE_CPLD_REG */ diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/slave_cpld_sysfs.h b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/slave_cpld_sysfs.h new file mode 100644 index 000000000000..92d00d29d493 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/slave_cpld_sysfs.h @@ -0,0 +1,155 @@ +#ifndef __SLAVE_CPLD_SYSFS +#define __SLAVE_CPLD_SYSFS + +/* generic CPLD sysfs file definition macros */ +#define SYSFS_RAW_RO_ATTR_DEF(field) \ +struct device_attribute field \ + = __ATTR(field, S_IRUGO, slave_cpld_##field##_raw_read, NULL); + +#define SYSFS_RAW_RW_ATTR_DEF(field) \ +struct device_attribute field \ + = __ATTR(field, S_IRUGO | S_IWUGO, slave_cpld_##field##_raw_read, slave_cpld_##field##_raw_write); + +#define SYSFS_MISC_RO_ATTR_DEF(field, _read) \ +struct device_attribute field \ + = __ATTR(field, S_IRUGO, _read, NULL); + +#define SYSFS_MISC_RW_ATTR_DEF(field, _read, _write) \ +struct device_attribute field \ + = __ATTR(field, S_IRUGO | S_IWUGO, _read, _write); + +#define SYSFS_ATTR_PTR(field) \ +&field.attr + + +/* declare slave CPLD file system */ +SYSFS_RAW_RO_ATTR_DEF(mjr_rev) +SYSFS_RAW_RO_ATTR_DEF(mnr_rev) + +SYSFS_RAW_RW_ATTR_DEF(scrtch_reg) + +SYSFS_RAW_RO_ATTR_DEF(brd_rev) +SYSFS_RAW_RO_ATTR_DEF(brd_type) + +SYSFS_RAW_RO_ATTR_DEF(port24_present) +SYSFS_RAW_RO_ATTR_DEF(port23_present) +SYSFS_RAW_RO_ATTR_DEF(port22_present) +SYSFS_RAW_RO_ATTR_DEF(port21_present) +SYSFS_RAW_RO_ATTR_DEF(port20_present) +SYSFS_RAW_RO_ATTR_DEF(port19_present) +SYSFS_RAW_RO_ATTR_DEF(port18_present) +SYSFS_RAW_RO_ATTR_DEF(port17_present) + +SYSFS_RAW_RO_ATTR_DEF(port32_present) +SYSFS_RAW_RO_ATTR_DEF(port31_present) +SYSFS_RAW_RO_ATTR_DEF(port30_present) +SYSFS_RAW_RO_ATTR_DEF(port29_present) +SYSFS_RAW_RO_ATTR_DEF(port28_present) +SYSFS_RAW_RO_ATTR_DEF(port27_present) +SYSFS_RAW_RO_ATTR_DEF(port26_present) +SYSFS_RAW_RO_ATTR_DEF(port25_present) + +SYSFS_RAW_RW_ATTR_DEF(port24_rst) +SYSFS_RAW_RW_ATTR_DEF(port23_rst) +SYSFS_RAW_RW_ATTR_DEF(port22_rst) +SYSFS_RAW_RW_ATTR_DEF(port21_rst) +SYSFS_RAW_RW_ATTR_DEF(port20_rst) +SYSFS_RAW_RW_ATTR_DEF(port19_rst) +SYSFS_RAW_RW_ATTR_DEF(port18_rst) +SYSFS_RAW_RW_ATTR_DEF(port17_rst) + +SYSFS_RAW_RW_ATTR_DEF(port32_rst) +SYSFS_RAW_RW_ATTR_DEF(port31_rst) +SYSFS_RAW_RW_ATTR_DEF(port30_rst) +SYSFS_RAW_RW_ATTR_DEF(port29_rst) +SYSFS_RAW_RW_ATTR_DEF(port28_rst) +SYSFS_RAW_RW_ATTR_DEF(port27_rst) +SYSFS_RAW_RW_ATTR_DEF(port26_rst) +SYSFS_RAW_RW_ATTR_DEF(port25_rst) + +SYSFS_RAW_RW_ATTR_DEF(port24_modsel) +SYSFS_RAW_RW_ATTR_DEF(port23_modsel) +SYSFS_RAW_RW_ATTR_DEF(port22_modsel) +SYSFS_RAW_RW_ATTR_DEF(port21_modsel) +SYSFS_RAW_RW_ATTR_DEF(port20_modsel) +SYSFS_RAW_RW_ATTR_DEF(port19_modsel) +SYSFS_RAW_RW_ATTR_DEF(port18_modsel) +SYSFS_RAW_RW_ATTR_DEF(port17_modsel) + +SYSFS_RAW_RW_ATTR_DEF(port32_modsel) +SYSFS_RAW_RW_ATTR_DEF(port31_modsel) +SYSFS_RAW_RW_ATTR_DEF(port30_modsel) +SYSFS_RAW_RW_ATTR_DEF(port29_modsel) +SYSFS_RAW_RW_ATTR_DEF(port28_modsel) +SYSFS_RAW_RW_ATTR_DEF(port27_modsel) +SYSFS_RAW_RW_ATTR_DEF(port26_modsel) +SYSFS_RAW_RW_ATTR_DEF(port25_modsel) + +SYSFS_RAW_RW_ATTR_DEF(port24_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port23_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port22_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port21_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port20_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port19_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port18_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port17_lpmode) + +SYSFS_RAW_RW_ATTR_DEF(port32_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port31_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port30_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port29_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port28_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port27_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port26_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port25_lpmode) + +SYSFS_RAW_RO_ATTR_DEF(port24_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port23_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port22_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port21_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port20_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port19_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port18_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port17_irq_status) + +SYSFS_RAW_RO_ATTR_DEF(port32_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port31_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port30_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port29_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port28_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port27_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port26_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port25_irq_status) + +SYSFS_RAW_RW_ATTR_DEF(port24_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port23_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port22_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port21_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port20_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port19_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port18_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port17_irq_msk) + +SYSFS_RAW_RW_ATTR_DEF(port32_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port31_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port30_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port29_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port28_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port27_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port26_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port25_irq_msk) + +SYSFS_RAW_RO_ATTR_DEF(port_17_24_present) +SYSFS_RAW_RO_ATTR_DEF(port_25_32_present) +SYSFS_RAW_RW_ATTR_DEF(port_17_24_rst) +SYSFS_RAW_RW_ATTR_DEF(port_25_32_rst) +SYSFS_RAW_RW_ATTR_DEF(port_17_24_modsel) +SYSFS_RAW_RW_ATTR_DEF(port_25_32_modsel) +SYSFS_RAW_RO_ATTR_DEF(port_17_24_irq_status) +SYSFS_RAW_RO_ATTR_DEF(port_25_32_irq_status) +SYSFS_RAW_RW_ATTR_DEF(port_17_24_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port_25_32_irq_msk) +SYSFS_RAW_RW_ATTR_DEF(port_17_24_lpmode) +SYSFS_RAW_RW_ATTR_DEF(port_25_32_lpmode) + +#endif /* __SLAVE_CPLD_SYSFS */ diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/system_cpld_reg.h b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/system_cpld_reg.h new file mode 100644 index 000000000000..ba66519efe8a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/system_cpld_reg.h @@ -0,0 +1,107 @@ +#ifndef __SYSTEM_CPLD_REG +#define __SYSTEM_CPLD_REG + +static int system_cpld_raw_read(struct device *dev, struct device_attribute *attr, char *buf, + int reg_offset, int reg_width, int fld_shift, int fld_width, int fld_mask, char* reg_name); +static int system_cpld_raw_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count, + int reg_offset, int reg_width, int fld_shift, int fld_width, int fld_mask, char* reg_name); + +/* generic CPLD read function */ +#define FLD_RAW_RD_FUNC(_reg, _fld, _wdh) static ssize_t \ +system_cpld_##_fld##_raw_read(struct device *dev, struct device_attribute *attr, char *buf) { \ + return system_cpld_raw_read(dev, attr, buf, _reg##_offset, _reg##_width, _fld##_shift, _fld##_width, _fld##_mask, #_reg); \ +} + +/* generic CPLD write function */ +#define FLD_RAW_WR_FUNC(_reg, _fld, _wdh) static ssize_t \ +system_cpld_##_fld##_raw_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { \ + return system_cpld_raw_write(dev, attr, buf, count, _reg##_offset, _reg##_width, _fld##_shift, _fld##_width, _fld##_mask, #_reg); \ +} + +/* CPLD register definition macros */ +#define REG_DEF(_reg, _off, _wdh) \ +static unsigned int _reg##_offset = (unsigned int)(_off); \ +static unsigned int _reg##_width = (unsigned int)(_wdh); + +/* CPLD register field definition macros, with generic read/write function */ +#define FLD_RAW_RO_DEF(_reg, _fld, _sft, _wdh) \ +static unsigned int _fld##_shift = (unsigned int)(_sft); \ +static unsigned int _fld##_width = (unsigned int)(_wdh); \ +static unsigned int _fld##_mask = ((((unsigned int)1) << (_wdh)) - 1); \ +FLD_RAW_RD_FUNC(_reg, _fld, _wdh) + +#define FLD_RAW_RW_DEF(_reg, _fld, _sft, _wdh) \ +static unsigned int _fld##_shift = (unsigned int)(_sft); \ +static unsigned int _fld##_width = (unsigned int)(_wdh); \ +static unsigned int _fld##_mask = ((((unsigned int)1) << (_wdh)) - 1); \ +FLD_RAW_RD_FUNC(_reg, _fld, _wdh) FLD_RAW_WR_FUNC(_reg, _fld, _wdh) + +/* declare system CPLD registers */ +/* register name offset width */ +/* --------------------------------------- ------- ----- */ +REG_DEF( sys_cpld_rev, 0x00, 8) +REG_DEF( sys_cpld_gpr, 0x01, 8) +REG_DEF( cpu_brd_rev_type, 0x02, 8) +REG_DEF( sys_srr, 0x03, 8) +REG_DEF( sys_eeprom_wp, 0x04, 8) +REG_DEF( sys_irq, 0x05, 8) +REG_DEF( sys_wd, 0x06, 8) +REG_DEF( sys_mb_rst_en, 0x07, 8) +REG_DEF( cpu_pwr_en_status, 0x08, 8) +REG_DEF( cpu_pwr_status, 0x09, 8) + + +/* declare system CPLD register's fields */ +/* register name field name shift width */ +/* ---------------------- ---------------- ------ ----- */ +FLD_RAW_RO_DEF( sys_cpld_rev, mjr_rev, 4, 4) +FLD_RAW_RO_DEF( sys_cpld_rev, mnr_rev, 0, 4) + +FLD_RAW_RW_DEF( sys_cpld_gpr, scrtch_reg, 0, 8) + +FLD_RAW_RO_DEF( cpu_brd_rev_type, brd_rev, 4, 4) +FLD_RAW_RO_DEF( cpu_brd_rev_type, brd_type, 0, 4) + +FLD_RAW_RO_DEF( sys_srr, ssd_present, 3, 1) +FLD_RAW_RW_DEF( sys_srr, spi_cs_sel, 2, 1) +FLD_RAW_RW_DEF( sys_srr, rst_bios_switch, 1, 1) +FLD_RAW_RW_DEF( sys_srr, cpld_upgrade_rst, 0, 1) + +FLD_RAW_RW_DEF( sys_eeprom_wp, cpld_spi_wp, 4, 1) +FLD_RAW_RW_DEF( sys_eeprom_wp, system_id_eeprom_wp, 3, 1) +FLD_RAW_RW_DEF( sys_eeprom_wp, spi_me_wp, 2, 1) +FLD_RAW_RW_DEF( sys_eeprom_wp, spi_bios_wp, 1, 1) +FLD_RAW_RW_DEF( sys_eeprom_wp, spi_bak_bios_wp, 0, 1) + +FLD_RAW_RW_DEF( sys_irq, vrhot_irq_en, 6, 1) +FLD_RAW_RW_DEF( sys_irq, cpu_thermtrip_irq_en,5, 1) +FLD_RAW_RW_DEF( sys_irq, temp_alert_irq_en, 4, 1) +FLD_RAW_RO_DEF( sys_irq, vrhot_irq, 2, 1) +FLD_RAW_RO_DEF( sys_irq, cpu_thermtrip_irq, 1, 1) +FLD_RAW_RO_DEF( sys_irq, temp_alert_irq, 0, 1) + +FLD_RAW_RW_DEF( sys_wd, wd_timer, 4, 4) +FLD_RAW_RW_DEF( sys_wd, wd_en, 1, 1) +FLD_RAW_RW_DEF( sys_wd, wd_punch, 0, 1) + +FLD_RAW_RW_DEF( sys_mb_rst_en, mb_rst_en, 0, 1) + +FLD_RAW_RO_DEF( cpu_pwr_en_status, pwr_v3p3_en, 7, 1) +FLD_RAW_RO_DEF( cpu_pwr_en_status, pwr_vcc_vnn_en, 6, 1) +FLD_RAW_RO_DEF( cpu_pwr_en_status, pwr_vccsram_en, 5, 1) +FLD_RAW_RO_DEF( cpu_pwr_en_status, pwr_vddq_en, 4, 1) +FLD_RAW_RO_DEF( cpu_pwr_en_status, pwr_vcc_ref_en, 3, 1) +FLD_RAW_RO_DEF( cpu_pwr_en_status, pwr_v1p05_en, 2, 1) +FLD_RAW_RO_DEF( cpu_pwr_en_status, pwr_v1p8_en, 1, 1) +FLD_RAW_RO_DEF( cpu_pwr_en_status, pwr_v2p5_en, 0, 1) + +FLD_RAW_RO_DEF( cpu_pwr_status, pg_v3p3, 7, 1) +FLD_RAW_RO_DEF( cpu_pwr_status, pg_vcc_vnn, 6, 1) +FLD_RAW_RO_DEF( cpu_pwr_status, pg_vccsram, 5, 1) +FLD_RAW_RO_DEF( cpu_pwr_status, pg_vddq, 4, 1) +FLD_RAW_RO_DEF( cpu_pwr_status, pg_vcc_ref, 3, 1) +FLD_RAW_RO_DEF( cpu_pwr_status, pg_v1p05, 2, 1) +FLD_RAW_RO_DEF( cpu_pwr_status, pg_v1p8, 1, 1) +FLD_RAW_RO_DEF( cpu_pwr_status, pg_v2p5, 0, 1) + +#endif /* __SYSTEM_CPLD_REG */ diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/system_cpld_sysfs.h b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/system_cpld_sysfs.h new file mode 100644 index 000000000000..e6b03bd1bd0d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/modules/system_cpld_sysfs.h @@ -0,0 +1,76 @@ +#ifndef __SYSTEM_CPLD_SYSFS +#define __SYSTEM_CPLD_SYSFS + +/* generic CPLD sysfs file definition macros */ +#define SYSFS_RAW_RO_ATTR_DEF(field) \ +struct device_attribute field \ + = __ATTR(field, S_IRUGO, system_cpld_##field##_raw_read, NULL); + +#define SYSFS_RAW_RW_ATTR_DEF(field) \ +struct device_attribute field \ + = __ATTR(field, S_IRUGO | S_IWUGO, system_cpld_##field##_raw_read, system_cpld_##field##_raw_write); + +#define SYSFS_MISC_RO_ATTR_DEF(field, _read) \ +struct device_attribute field \ + = __ATTR(field, S_IRUGO, _read, NULL); + +#define SYSFS_MISC_RW_ATTR_DEF(field, _read, _write) \ +struct device_attribute field \ + = __ATTR(field, S_IRUGO | S_IWUGO, _read, _write); + +#define SYSFS_ATTR_PTR(field) \ +&field.attr + + +/* declare system CPLD file system */ +SYSFS_RAW_RO_ATTR_DEF(mjr_rev) +SYSFS_RAW_RO_ATTR_DEF(mnr_rev) + +SYSFS_RAW_RW_ATTR_DEF(scrtch_reg) + +SYSFS_RAW_RO_ATTR_DEF(brd_rev) +SYSFS_RAW_RO_ATTR_DEF(brd_type) + +SYSFS_RAW_RO_ATTR_DEF(ssd_present) +SYSFS_RAW_RW_ATTR_DEF(spi_cs_sel) +SYSFS_RAW_RW_ATTR_DEF(rst_bios_switch) +SYSFS_RAW_RW_ATTR_DEF(cpld_upgrade_rst) + +SYSFS_RAW_RW_ATTR_DEF(cpld_spi_wp) +SYSFS_RAW_RW_ATTR_DEF(system_id_eeprom_wp) +SYSFS_RAW_RW_ATTR_DEF(spi_me_wp) +SYSFS_RAW_RW_ATTR_DEF(spi_bios_wp) +SYSFS_RAW_RW_ATTR_DEF(spi_bak_bios_wp) + +SYSFS_RAW_RW_ATTR_DEF(vrhot_irq_en) +SYSFS_RAW_RW_ATTR_DEF(cpu_thermtrip_irq_en) +SYSFS_RAW_RW_ATTR_DEF(temp_alert_irq_en) +SYSFS_RAW_RO_ATTR_DEF(vrhot_irq) +SYSFS_RAW_RO_ATTR_DEF(cpu_thermtrip_irq) +SYSFS_RAW_RO_ATTR_DEF(temp_alert_irq) + +SYSFS_RAW_RW_ATTR_DEF(wd_timer) +SYSFS_RAW_RW_ATTR_DEF(wd_en) +SYSFS_RAW_RW_ATTR_DEF(wd_punch) + +SYSFS_RAW_RW_ATTR_DEF(mb_rst_en) + +SYSFS_RAW_RO_ATTR_DEF(pwr_v3p3_en) +SYSFS_RAW_RO_ATTR_DEF(pwr_vcc_vnn_en) +SYSFS_RAW_RO_ATTR_DEF(pwr_vccsram_en) +SYSFS_RAW_RO_ATTR_DEF(pwr_vddq_en) +SYSFS_RAW_RO_ATTR_DEF(pwr_vcc_ref_en) +SYSFS_RAW_RO_ATTR_DEF(pwr_v1p05_en) +SYSFS_RAW_RO_ATTR_DEF(pwr_v1p8_en) +SYSFS_RAW_RO_ATTR_DEF(pwr_v2p5_en) + +SYSFS_RAW_RO_ATTR_DEF(pg_v3p3) +SYSFS_RAW_RO_ATTR_DEF(pg_vcc_vnn) +SYSFS_RAW_RO_ATTR_DEF(pg_vccsram) +SYSFS_RAW_RO_ATTR_DEF(pg_vddq) +SYSFS_RAW_RO_ATTR_DEF(pg_vcc_ref) +SYSFS_RAW_RO_ATTR_DEF(pg_v1p05) +SYSFS_RAW_RO_ATTR_DEF(pg_v1p8) +SYSFS_RAW_RO_ATTR_DEF(pg_v2p5) + +#endif /* __SYSTEM_CPLD_SYSFS */ diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-ctrl b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-ctrl new file mode 100755 index 000000000000..73ff759b6f45 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-ctrl @@ -0,0 +1,679 @@ +#!/bin/bash +#/* +#********************************************************************** +#* +#* @filename fan-ctrl +#* +#* @purpose system daemon for controlling system fan pwm +#* +#* @create 2017/06/21 +#* +#* @author nixon.chu +#* +#* @history 2017/06/21: init version +#* +#********************************************************************** +#*/ + +DIR=$(dirname $0) + +# include files +source ${DIR}/funcs.sh + +#/* +#********************************************************************** +#* +#* CONSTANT VARIABLES +#* +#********************************************************************** +#*/ + +# process name/id +DAEMON_NAME=`basename $0` +DAEMON_PID="$$" + +# define symbol for fan/temperature type sensor +SYMBOL_TEMP="TEMP_" +SYMBOL_FAN="FAN_" + +# describe the structure of temperature sensor by id +STRUCT_TEMP_NAME=0 +STRUCT_TEMP_CMD=1 +STRUCT_TEMP_MAX=2 +STRUCT_TEMP_SIZE=$(( ${STRUCT_TEMP_MAX} + 1 )) + +# describe the structure of fan sensor by id +STRUCT_FAN_CMD=0 +STRUCT_FAN_SIZE=$(( ${STRUCT_FAN_CMD} + 1 )) + +# default fan zone configuration file +DEF_ZONE_CONF="${DIR}/fan-zone.conf" + +# default fan zone thermal configuration file +DEF_TEMP_CONF="${DIR}/fan-zone-thermal.conf" + +# default interval (sec) +DEF_INTERVAL=10 + +# default hysteresis (deg C) +DEF_HYSIS=3 + +# default debug mode (0: OFF, 1: ON) +DEF_DEBUG_MODE=0 + +# default log file +DEF_LOG_FILE="/var/log/syslog" + +# default fan level +DEF_FAN_LEVEL=0 + +# default minimum pwm (38.25=255x15%) +DEF_MIN_PWM=39 + +# default zone id +DEF_ZONE_ID=0 + +# error codes +E_STATUS_GOOD=0 +E_STATUS_FAULT=1 +E_INVALID_ARGS=11 +E_INVALID_CONF=12 + +# message levels +MSG_EMERG=0 +MSG_ALERT=1 +MSG_CRIT=2 +MSG_ERR=3 +MSG_WARNING=4 +MSG_NOTICE=5 +MSG_INFO=6 +MSG_DEBUG=7 + +# default message level +DEF_MSG_LEVEL=$MSG_WARNING + +#/* +#********************************************************************** +#* +#* GLOBAL VARIABLES +#* +#********************************************************************** +#*/ + +# temperature sensor group +GBL_TEMP_GRP=() +GBL_TEMP_NUM=${#GBL_TEMP_GRP[@]} + +# fan sensor group +GBL_FAN_GRP=() +GBL_FAN_NUM=${#GBL_FAN_GRP[@]} + +# fan level table +GBL_FAN_LEVEL=() +GBL_LEVEL_NUM=${#GBL_FAN_LEVEL[@]} + +# load defaults +GBL_ZONE_CONF=$DEF_ZONE_CONF +GBL_TEMP_CONF=$DEF_TEMP_CONF +GBL_INTERVAL=$DEF_INTERVAL +GBL_HYSIS=$DEF_HYSIS +GBL_DEBUG_MODE=$DEF_DEBUG_MODE +GBL_LOG_FILE=$DEF_LOG_FILE +GBL_CUR_LEVEL=$DEF_FAN_LEVEL +GBL_MIN_PWM=$DEF_MIN_PWM +GBL_ZONE_ID=$DEF_ZONE_ID +GBL_MSG_LEVEL=$DEF_MSG_LEVEL + +# temperature sensors' readings/statuses/properties (properties list have to be converted with fan level table) +GBL_TEMP_READINGS=() +GBL_TEMP_STATUSES=() +GBL_TEMP_PROPERTY=() + +#/* +#********************************************************************** +#* +#* FUNCTIONS +#* +#********************************************************************** +#*/ + +#/* +#* FEATURE: +#* usage +#* PURPOSE: +#* show the usage of this daemon +#* PARAMETERS: +#* +#* RETURNS: +#* success, this function returns @E_INVALID_ARGS. +#*/ +function usage() { + local dbg_mode + + [ $GBL_DEBUG_MODE -eq 0 ] && dbg_mode="off" || dbg_mode="on" + + echo -e "Usage: $0 [-z zone_file] [-t thermal_file] [-o log_file] [-d]" >&2 + echo -e "" >&2 + echo -e "Arguments:" >&2 + echo -e " -z, --zone-config Fan zone configuration file (default: $DEF_ZONE_CONF)" >&2 + echo -e " -t, --temp-config Fan zone thermal configuration file (default: $DEF_TEMP_CONF)" >&2 + echo -e " -o, --log-file Log file (default: $DEF_LOG_FILE)" >&2 + echo -e " -d, --debug Debug mode (default: $dbg_mode)" >&2 + exit ${E_INVALID_ARGS} +} + +#/* +#* FEATURE: +#* print_msg +#* PURPOSE: +#* print message by message level +#* PARAMETERS: +#* msg_lvl (IN) message level +#* msg (IN) message +#* RETURNS: +#* +#*/ +function print_msg() { + local msg_lvl=$1 + local msg=$2 + + [ $msg_lvl -le $GBL_MSG_LEVEL ] && echo "${msg}" >&2 +} + +#/* +#* FEATURE: +#* err_msg +#* PURPOSE: +#* log error message +#* PARAMETERS: +#* msg (IN) message +#* err_no (IN) error code +#* RETURNS: +#* success, this function returns non-zero error code. +#*/ +function err_msg() { + local msg=$1 + local err_no=$2 + + log_msg $MSG_ERROR "${msg}" + exit ${err_no} +} + +#/* +#* FEATURE: +#* log_msg +#* PURPOSE: +#* log message +#* PARAMETERS: +#* msg_lvl (IN) message level +#* msg (IN) message +#* RETURNS: +#* +#*/ +function log_msg() { + local msg_lvl=$1 + local msg=$2 + + if [ $GBL_LOG_FILE == $DEF_LOG_FILE ]; then + `logger -t $DAEMON_NAME -p $msg_lvl $msg` + else + echo -e "`date +"%b %_d %T"` `hostname` $DAEMON_NAME[$DAEMON_PID]: ${msg}" >> ${GBL_LOG_FILE} + fi + + print_msg $msg_lvl "${msg}" +} + +#/* +#* FEATURE: +#* debug_temp_grp +#* PURPOSE: +#* debug function for showing the temperature sensor group configs +#* @GBL_TEMP_GRP[]: store sensor group configs (sensor name, command, and max_temp) +#* PARAMETERS: +#* +#* RETURNS: +#* +#*/ +function debug_temp_grp() { + if [ $GBL_DEBUG_MODE -ne 0 ]; then + echo "********* Sensor Group *********" + for ((i=0; i<$GBL_TEMP_NUM; i++)) + do + echo -en "`fetch_temp_struct $i $STRUCT_TEMP_NAME`\t" + echo -en "`fetch_temp_struct $i $STRUCT_TEMP_CMD`\t" + echo "`fetch_temp_struct $i $STRUCT_TEMP_MAX`" + done + fi +} + +#/* +#* FEATURE: +#* debug_temp_readings +#* PURPOSE: +#* debug function for showing the temperature sensors' readings +#* @GBL_TEMP_READINGS[]: store current reading for temperature sensors +#* PARAMETERS: +#* +#* RETURNS: +#* +#*/ +function debug_temp_readings() { + if [ $GBL_DEBUG_MODE -ne 0 ]; then + echo "********* Sensor Reading *********" + for ((i=0; i<$GBL_TEMP_NUM; i++)) + do + echo -e "`fetch_temp_struct $i $STRUCT_TEMP_NAME`\t${GBL_TEMP_READINGS[$i]}" + done + fi +} + +#/* +#* FEATURE: +#* debug_temp_property +#* PURPOSE: +#* debug function for showing the temperature sensors' properties +#* @GBL_TEMP_PROPERTY[]: store temperature sensors' properties (asserted temperature for each fan levels) +#* PARAMETERS: +#* +#* RETURNS: +#* +#*/ +function debug_temp_property() { + local size base start_idx level + if [ $GBL_DEBUG_MODE -ne 0 ]; then + echo "********* Sensor Table *********" + for ((i=0; i<${GBL_TEMP_NUM}; i++)) + do + # show array @GBL_TEMP_PROPERTY[]: format name(1) + fan levels(2-?) + size=$(( ${GBL_LEVEL_NUM}+1 )) + base=$i*${size} + start_idx=$(( $base+1 )) + level=${GBL_LEVEL_NUM} + echo -e "${GBL_TEMP_PROPERTY[$base]}\t\t${GBL_TEMP_PROPERTY[@]:$start_idx:$level}" + done + fi +} + +#/* +#* FEATURE: +#* fetch_temp_struct +#* PURPOSE: +#* fetch the member value of specific temperature sensor +#* PARAMETERS: +#* id (IN) sensor id +#* member_id (IN) member id +#* RETURNS: +#* success, returns member value +#*/ +function fetch_temp_struct() { + local id member_id index_base data + + id=$1 + member_id=$2 + + index_base=$(( $id * $STRUCT_TEMP_SIZE )) + data=${GBL_TEMP_GRP[$(($index_base+$member_id))]} + # remove unuseful characters, such as double quotes('"') and the start/end space(' ') of a string. + data=`echo $data | sed 's/^ *\| *$//g' | sed 's/^"\|\"$//g'` + echo $data +} + +#/* +#* FEATURE: +#* fetch_temp_property +#* PURPOSE: +#* fetch the properties of specific temperature sensor +#* PARAMETERS: +#* name (IN) sensor name +#* RETURNS: +#* success, returns an asserted temperature list +#*/ +function fetch_temp_property() { + local name size base start_idx length + + name=$1 + + size=$(( $GBL_LEVEL_NUM+1 )) + for ((i=0; i<${GBL_TEMP_NUM}; i++)) + do + base=$i*${size} + # find sensor by name + if [ ${GBL_TEMP_PROPERTY[${base}]} == "${name}" ]; then + start_idx=$(( $base+1 )) + length=$GBL_LEVEL_NUM + echo ${GBL_TEMP_PROPERTY[@]:$start_idx:$length} + break + fi + done +} + +#/* +#* FEATURE: +#* debug_fan_speed +#* PURPOSE: +#* debug function for showing system current fan level and PWM +#* @GBL_CUR_LEVEL: system current/output fan level +#* PARAMETERS: +#* +#* RETURNS: +#* +#*/ +function debug_fan_speed() { + if [ $GBL_DEBUG_MODE -ne 0 ]; then + # For human readable, the representation of fan level will start from 1. + echo "Current Level: $(( $GBL_CUR_LEVEL+1 )), PWM: ${GBL_FAN_LEVEL[$GBL_CUR_LEVEL]}" + fi +} + +#/* +#* FEATURE: +#* arg_parse +#* PURPOSE: +#* parser for input arguments +#* PARAMETERS: +#* arg_lists[] (IN) argument list +#* RETURNS: +#* +#*/ +function arg_parse() { + while [[ $# -ge 1 ]] + do + key="$1" + case $key in + -z|--zone-config) + [ $# -lt 2 ] && usage + GBL_ZONE_CONF=$2 + [ ! -e $GBL_ZONE_CONF ] && usage + shift # past argument + ;; + -t|--temp-config) + [ $# -lt 2 ] && usage + GBL_TEMP_CONF=$2 + [ ! -e $GBL_TEMP_CONF ] && usage + shift # past argument + ;; + -o|--log-file) + [ $# -lt 2 ] && usage + GBL_LOG_FILE=$2 + shift # past argument + ;; + -d|--debug) + GBL_DEBUG_MODE=1 + ;; + *) + usage # unknown option + ;; + esac + shift # past argument or value + done +} + +#/* +#* FEATURE: +#* valid_conf +#* PURPOSE: +#* check if configuration files are valid +#* PARAMETERS: +#* +#* RETURNS: +#* success, this function returns nothing +#* fail, returns @E_INVALID_CONF +#*/ +function valid_conf() { + local list_1 list_2 match_num + + list_1=("${!1}") + list_2=("${!2}") + + # validate zone config + [ ${GBL_TEMP_NUM} -eq 0 ] && err_msg "config: status=invalid. reason=no TEMPERATURE SENSORS defined." "${E_INVALID_CONF}" + [ ${GBL_FAN_NUM} -eq 0 ] && err_msg "config: status=invalid. reason=no FAN defined." "${E_INVALID_CONF}" + + # validate thermal config + [ ${GBL_LEVEL_NUM} -eq 0 ] && err_msg "config: status=invalid. reason=no FAN LEVELS defined." "${E_INVALID_CONF}" + [ ${#list_2[@]} -ne ${GBL_TEMP_NUM} ] && err_msg "config: status=invalid. reason=the number of temperature sensors is inconsistent." "${E_INVALID_CONF}" + + # compare temperature sensor name between configuration files + match_num=0 + for i in ${!list_1[@]} + do + for j in ${!list_2[@]} + do + [ "${list_1[$i]}" == "${list_2[$j]}" ] && match_num=$(( $match_num+1 )) + done + done + [ $match_num -ne ${GBL_TEMP_NUM} ] && err_msg "config: status=invalid. reason=the name of temperature sensors is inconsistent." "${E_INVALID_CONF}" + + # validate pwm values + for pwm in ${GBL_FAN_LEVEL[@]} + do + expr $pwm + $GBL_MIN_PWM >/dev/null 2>&1 + [ $? -ne 0 ] && err_msg "config: status=invalid. reason=the value of pwm is not integer." "$E_INVALID_CONF" + [ $pwm -lt $GBL_MIN_PWM ] && err_msg "config: status=invalid. reason=the value of fan level is less than MIN_PWM($GBL_MIN_PWM)." "$E_INVALID_CONF" + done +} + +#/* +#* FEATURE: +#* initialize +#* PURPOSE: +#* load zone config and thermal config +#* PARAMETERS: +#* +#* RETURNS: +#* +#*/ +function initialize() { + local data conf_interval conf_hysis conf_min_pwm conf_zone_id conf_msg_level temp_list_1 temp_list_2 + + log_msg $MSG_INFO "Initializing fan control service..." + + # fetch temperature sensor name from configuration files + temp_list_1=(`cat ${GBL_ZONE_CONF} | grep "^${SYMBOL_TEMP}" | awk -F "," {'print $1'}`) + temp_list_2=(`cat ${GBL_TEMP_CONF} | grep "^${SYMBOL_TEMP}" | awk {'print $1'}`) + + # calculate the number of temperature/fan sensors + GBL_TEMP_NUM=${#temp_list_1[@]} + GBL_FAN_NUM=(`cat ${GBL_ZONE_CONF} | grep "^${SYMBOL_FAN}" | awk {'print $2'} | wc -l`) + + # calculate the number of fan levels + GBL_FAN_LEVEL=(`cat ${GBL_TEMP_CONF} | grep "^PWM" | awk -F "PWM" {'print $2'}`) + GBL_LEVEL_NUM=${#GBL_FAN_LEVEL[@]} + + # check if the interval/hyteresis should be updated. + conf_interval=`cat ${GBL_ZONE_CONF} | grep "^INTERVAL=" | awk -F "=" {'print $2'} | tail` + [ ! -z ${conf_interval} ] && GBL_INTERVAL=${conf_interval} + conf_hysis=`cat ${GBL_ZONE_CONF} | grep "^HYTERESIS=" | awk -F "=" {'print $2'} | tail` + [ ! -z ${conf_hysis} ] && GBL_HYSIS=${conf_hysis} + conf_min_pwm=`cat ${GBL_ZONE_CONF} | grep "^MIN_PWM=" | awk -F "=" {'print $2'} | tail` + [ ! -z ${conf_min_pwm} ] && GBL_MIN_PWM=${conf_min_pwm} + conf_zone_id=`cat ${GBL_ZONE_CONF} | grep "^ZONE_ID=" | awk -F "=" {'print $2'} | tail` + [ ! -z ${conf_zone_id} ] && GBL_ZONE_ID=${conf_zone_id} + conf_msg_level=`cat ${GBL_ZONE_CONF} | grep "^MSG_LEVEL=" | awk -F "=" {'print $2'} | tail` + [ ! -z ${conf_msg_level} ] && GBL_MSG_LEVEL=${conf_msg_level} + + # load zone config + for ((i=1; i<=${GBL_TEMP_NUM}; i++)) + do + GBL_TEMP_STATUSES+=($E_STATUS_GOOD) + data=`cat ${GBL_ZONE_CONF} | grep "^${SYMBOL_TEMP}" | awk NR==$i` + # FIXME + GBL_TEMP_GRP+=("`echo $data | awk -F ", " {'print $1'}`") + GBL_TEMP_GRP+=("`echo $data | awk -F ", " {'print $2'}`") + GBL_TEMP_GRP+=("`echo $data | awk -F ", " {'print $3'}`") + done + debug_temp_grp + + for ((i=1; i<=${GBL_FAN_NUM}; i++)) + do + data=`cat ${GBL_ZONE_CONF} | grep "^${SYMBOL_FAN}" | awk NR==$i{'print $2'}` + GBL_FAN_GRP+=("`echo $data`") + done + + # load thermal config + for ((i=0; i<${GBL_TEMP_NUM}; i++)) + do + index=$(( $i * ${STRUCT_TEMP_SIZE} + ${STRUCT_TEMP_NAME} )) + sensor_name=${GBL_TEMP_GRP[$index]} + GBL_TEMP_PROPERTY+=(`cat ${GBL_TEMP_CONF} | grep "^${sensor_name}" | tail`) + done + debug_temp_property + + # check if the config files is valid + valid_conf temp_list_1[@] temp_list_2[@] +} + +#/* +#* FEATURE: +#* temperature_collection +#* PURPOSE: +#* collect all temperature sensor readlings +#* PARAMETERS: +#* +#* RETURNS: +#* success, returns a series of reading @GBL_TEMP_READINGS[] +#*/ +function temperature_collection() { + local name cmd max_temp reading status pre_status + + GBL_TEMP_READINGS=() + for ((i=0; i<${GBL_TEMP_NUM}; i++)) + do + # read temperature sensor value & record sensor status + name=`fetch_temp_struct $i ${STRUCT_TEMP_NAME}` + cmd=`fetch_temp_struct $i ${STRUCT_TEMP_CMD}` + max_temp=`fetch_temp_struct $i ${STRUCT_TEMP_MAX}` + reading=`eval ${cmd} 2>/dev/null` + if [ $? -eq 0 ]; then + GBL_TEMP_READINGS+=($reading) + status=$E_STATUS_GOOD + else + GBL_TEMP_READINGS+=($max_temp) + status=$E_STATUS_FAULT + fi + + # Compare previous status of temperature sensor, then update it. + pre_status=${GBL_TEMP_STATUSES[$i]} + if [ $pre_status -eq $E_STATUS_GOOD ] && [ $status -eq $E_STATUS_FAULT ]; then + # Good -> Fault + log_msg $MSG_WARN "[ZONE_${GBL_ZONE_ID}] Sensor $name: status=ERROR. reason=read sensor failed." + elif [ $pre_status -eq $E_STATUS_FAULT ] && [ $status -eq $E_STATUS_GOOD ]; then + # Fault -> Good + log_msg $MSG_WARN "[ZONE_${GBL_ZONE_ID}] Sensor $name: status=RECOVER. reason=reading is $reading deg C" + fi + GBL_TEMP_STATUSES[$i]=$status + done + debug_temp_readings +} + +#/* +#* FEATURE: +#* fan_level_selection +#* PURPOSE: +#* select proper fan level by temperature sensor reading +#* PARAMETERS: +#* reading_list (IN) sensor reading list +#* RETURNS: +#* success, returns a fan level value +#*/ +function fan_level_selection() { + local reading_list name reading temp_list highest_level pre_level next_level hysis_temp res res1 + + reading_list=("${!1}") + highest_level=0 + pre_level=${GBL_CUR_LEVEL} + next_level=0 + + # search fan table by each temperature sensor reading + for ((i=0; i<${GBL_TEMP_NUM}; i++)) + do + name=`fetch_temp_struct $i $STRUCT_TEMP_NAME` + reading=${reading_list[$i]} + temp_list=(`fetch_temp_property ${name}`) + for j in ${!temp_list[@]} + do + res=`echo $reading \<= ${temp_list[$j]} | bc -l` + if [ $res -eq 1 ] || [ $j -eq $((${#temp_list[@]}-1)) ]; then + if [ $j -gt $highest_level ]; then + highest_level=$j + fi + break + fi + done + done + + if [ $highest_level -lt $pre_level ]; then + # Determine if require decreasing current fan level @assert_level + local pass_num + pass_num=0 + for ((i=0; i<${GBL_TEMP_NUM}; i++)) + do + name=`fetch_temp_struct $i $STRUCT_TEMP_NAME` + reading=${reading_list[$i]} + temp_list=(`fetch_temp_property ${name}`) + assert_level=$(( $pre_level-1 )) + hysis_temp=`echo ${temp_list[$assert_level]} - $GBL_HYSIS | bc` + res=`echo $reading \<= $hysis_temp | bc -l` + if [ $res -eq 1 ]; then + pass_num=$(( $pass_num+1 )) + else + break + fi + done + if [ $pass_num -eq ${GBL_TEMP_NUM} ]; then + # Decrease fan pwm: All sensor reading should less than its own hysteresis temperature + next_level=${highest_level} + else + # Keep current fan pwm + next_level=${pre_level} + fi + else + # Increase fan pwm + next_level=${highest_level} + fi + GBL_CUR_LEVEL=${next_level} +} + +#/* +#* FEATURE: +#* apply_fan_level +#* PURPOSE: +#* convert given fan level to PWM, then output to fans +#* PARAMETERS: +#* fan_list (IN) fan devices list +#* fan_level (IN) fan level +#* RETURNS: +#* success, returns a pwm value +#*/ +function apply_fan_level() { + local fan_level fan_list + + fan_list=("${!1}") + fan_level=$2 + + for fan in ${fan_list[@]} + do + echo ${GBL_FAN_LEVEL[$fan_level]} > $fan + done + debug_fan_speed +} + +#/* +#********************************************************************** +#* +#* MAIN +#* +#********************************************************************** +#*/ +Platform_init +arg_parse $@ +initialize +log_msg $MSG_INFO "Starting fan control service..." +while [ true ]; do + temperature_collection + fan_level_selection GBL_TEMP_READINGS[@] + apply_fan_level GBL_FAN_GRP[@] ${GBL_CUR_LEVEL} + sleep $GBL_INTERVAL +done + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-zone-thermal_B2F.conf b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-zone-thermal_B2F.conf new file mode 100644 index 000000000000..fc37dda47f47 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-zone-thermal_B2F.conf @@ -0,0 +1,13 @@ +##### Temperature vs Fan table ##### +## FORMAT: "PWM" fan speed(range from 0 to 255) +# Duty Cycle 30% 32.5% 35% 37.5% 40% 45% 50% 55% 60% 65% 70% 75% 80% 90% 100% +PWM 77 83 89 95 102 114 128 141 153 166 179 192 204 230 255 +## FORMAT: sensor name(TEMP_XXX) with asserted temperature by each levels +TEMP_CPU 46 48 50 52 54 57 60 63 66 69 72 75 78 81 84 +TEMP_SWITCH 72 74 76 78 80 83 86 89 92 95 98 101 104 107 110 +#TEMP_HDD 25 +#TEMP_TMP75_CPU 25 +#TEMP_TMP75_CENTER 25 +#TEMP_TMP75_RIGHT 33 37 45 53 61 69 77 85 93 +TEMP_TMP75_FAN 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 +TEMP_XCVR 36 38 40 42 44 46 48 50 52 54 56 58 60 63 66 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-zone-thermal_F2B.conf b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-zone-thermal_F2B.conf new file mode 100644 index 000000000000..ee316c66bd06 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-zone-thermal_F2B.conf @@ -0,0 +1,12 @@ +##### Temperature vs Fan table ##### +## FORMAT: "PWM" fan speed(range from 0 to 255) +# Duty Cycle 29% 31% 33% 35% 37.5% 40% 45% 50% 55% 60% 65% 70% 75% 80% 90% 100% +PWM 74 79 84 89 96 102 115 128 140 153 166 179 191 204 230 255 +## FORMAT: sensor name(TEMP_XXX) with asserted temperature by each levels +TEMP_CPU 51 53 55 57 59 61 63 65 67 69 71 73 75 77 79 81 +TEMP_SWITCH 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97 99 +#TEMP_HDD 25 +#TEMP_TMP75_CPU 25 +#TEMP_TMP75_CENTER 25 +TEMP_TMP75_RIGHT 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 +#TEMP_TMP75_FAN 25 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-zone_B2F.conf b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-zone_B2F.conf new file mode 100644 index 000000000000..b51f3b7aa67d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-zone_B2F.conf @@ -0,0 +1,38 @@ +##### ZONE ID ##### +ZONE_ID=0 + +##### SENSOR GROUP START ##### +## FORMAT: name(TEMP_XXX), command, max_temp(deg C) +TEMP_CPU, "data=`sensors | grep coretemp -A2`; [ $? -ne 0 ] && exit 1; echo "$data" | tail -n1 | awk {'print $4'} | sed 's/^+\|°C//g'", 110 +TEMP_SWITCH, "[ `file /var/run/docker-syncd/sswsyncd.socket | grep -c " socket"` -ne 1 ] && exit 1; [ `ss -a | grep "/var/run/sswsyncd/sswsyncd.socket" |grep -c "LISTEN"` -ne 1 ] && exit 1; data=`bcmcmd -c "show temp" | grep "temperature monitor"`; [ $? -ne 0 ] && exit 1; switch_temps=(`echo "$data" | awk {'print $5'} | sed 's/,//g'`); high_temp=0; for reading in ${switch_temps[@]}; do res=`echo "$reading > $high_temp" | bc -l`; [ $res -eq 1 ] && high_temp=$reading; done; echo $high_temp", 120 +#TEMP_HDD, "data=`hddtemp /dev/sdb | grep -v "not available"`; [ $? -ne 0 ] && exit 1; echo "$data" | awk -F ": " {'print $3'} | sed 's/°C//g'", 85 +#TEMP_TMP75_CPU, "data=`sensors | grep tmp75-i2c-0-4e -A2`; [ $? -ne 0 ] && exit 1; echo "$data" | tail -n1 | awk {'print $2'} | sed 's/^+\|°C//g'", 125 +#TEMP_TMP75_CENTER, "data=`sensors | grep tmp75-i2c-4-4a -A2`; [ $? -ne 0 ] && exit 1; echo "$data" | tail -n1 | awk {'print $2'} | sed 's/^+\|°C//g'", 125 +#TEMP_TMP75_RIGHT, "data=`sensors | grep tmp75-i2c-4-4c -A2`; [ $? -ne 0 ] && exit 1; echo "$data" | tail -n1 | awk {'print $2'} | sed 's/^+\|°C//g'", 125 +TEMP_TMP75_FAN, "data=`sensors | grep tmp75-i2c-4-4d -A2`; [ $? -ne 0 ] && exit 1; echo "$data" | tail -n1 | awk {'print $2'} | sed 's/^+\|°C//g'", 125 +##### SENSOR GROUP END ##### + +##### FAN GROUP START ##### +## FORMAT: name(FAN_YYY) input_files +FAN_1 /sys/bus/i2c/devices/3-0020/hwmon/hwmon*/pwm1 +FAN_2 /sys/bus/i2c/devices/3-0020/hwmon/hwmon*/pwm2 +FAN_3 /sys/bus/i2c/devices/3-0020/hwmon/hwmon*/pwm3 +FAN_4 /sys/bus/i2c/devices/3-0020/hwmon/hwmon*/pwm4 +FAN_5 /sys/bus/i2c/devices/3-0020/hwmon/hwmon*/pwm5 +FAN_6 /sys/bus/i2c/devices/3-0020/hwmon/hwmon*/pwm6 +FAN_7 /sys/bus/i2c/devices/3-0023/hwmon/hwmon*/pwm1 +FAN_8 /sys/bus/i2c/devices/3-0023/hwmon/hwmon*/pwm2 +FAN_9 /sys/bus/i2c/devices/3-0023/hwmon/hwmon*/pwm3 +FAN_10 /sys/bus/i2c/devices/3-0023/hwmon/hwmon*/pwm4 +FAN_11 /sys/bus/i2c/devices/3-0023/hwmon/hwmon*/pwm5 +FAN_12 /sys/bus/i2c/devices/3-0023/hwmon/hwmon*/pwm6 +##### FAN GROUP END ##### + +##### INTERVAL (sec) ##### +INTERVAL=10 + +##### HYTERESIS (deg C) ##### +HYTERESIS=2 + +##### MINIMUM PWM (38.25=255x15%) ##### +MIN_PWM=39 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-zone_F2B.conf b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-zone_F2B.conf new file mode 100644 index 000000000000..2d5987c5c66b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/fan-zone_F2B.conf @@ -0,0 +1,38 @@ +##### ZONE ID ##### +ZONE_ID=0 + +##### SENSOR GROUP START ##### +## FORMAT: name(TEMP_XXX), command, max_temp(deg C) +TEMP_CPU, "data=`sensors | grep coretemp -A2`; [ $? -ne 0 ] && exit 1; echo "$data" | tail -n1 | awk {'print $4'} | sed 's/^+\|°C//g'", 110 +TEMP_SWITCH, "[ `file /var/run/docker-syncd/sswsyncd.socket | grep -c " socket"` -ne 1 ] && exit 1; [ `ss -a | grep "/var/run/sswsyncd/sswsyncd.socket" |grep -c "LISTEN"` -ne 1 ] && exit 1; data=`bcmcmd -c "show temp" | grep "temperature monitor"`; [ $? -ne 0 ] && exit 1; switch_temps=(`echo "$data" | awk {'print $5'} | sed 's/,//g'`); high_temp=0; for reading in ${switch_temps[@]}; do res=`echo "$reading > $high_temp" | bc -l`; [ $res -eq 1 ] && high_temp=$reading; done; echo $high_temp", 120 +#TEMP_HDD, "data=`hddtemp /dev/sdb | grep -v "not available"`; [ $? -ne 0 ] && exit 1; echo "$data" | awk -F ": " {'print $3'} | sed 's/°C//g'", 85 +#TEMP_TMP75_CPU, "data=`sensors | grep tmp75-i2c-0-4e -A2`; [ $? -ne 0 ] && exit 1; echo "$data" | tail -n1 | awk {'print $2'} | sed 's/^+\|°C//g'", 125 +#TEMP_TMP75_CENTER, "data=`sensors | grep tmp75-i2c-4-4a -A2`; [ $? -ne 0 ] && exit 1; echo "$data" | tail -n1 | awk {'print $2'} | sed 's/^+\|°C//g'", 125 +TEMP_TMP75_RIGHT, "data=`sensors | grep tmp75-i2c-4-4c -A2`; [ $? -ne 0 ] && exit 1; echo "$data" | tail -n1 | awk {'print $2'} | sed 's/^+\|°C//g'", 125 +#TEMP_TMP75_FAN, "data=`sensors | grep tmp75-i2c-4-4d -A2`; [ $? -ne 0 ] && exit 1; echo "$data" | tail -n1 | awk {'print $2'} | sed 's/^+\|°C//g'", 125 +##### SENSOR GROUP END ##### + +##### FAN GROUP START ##### +## FORMAT: name(FAN_YYY) input_files +FAN_1 /sys/bus/i2c/devices/3-0020/hwmon/hwmon*/pwm1 +FAN_2 /sys/bus/i2c/devices/3-0020/hwmon/hwmon*/pwm2 +FAN_3 /sys/bus/i2c/devices/3-0020/hwmon/hwmon*/pwm3 +FAN_4 /sys/bus/i2c/devices/3-0020/hwmon/hwmon*/pwm4 +FAN_5 /sys/bus/i2c/devices/3-0020/hwmon/hwmon*/pwm5 +FAN_6 /sys/bus/i2c/devices/3-0020/hwmon/hwmon*/pwm6 +FAN_7 /sys/bus/i2c/devices/3-0023/hwmon/hwmon*/pwm1 +FAN_8 /sys/bus/i2c/devices/3-0023/hwmon/hwmon*/pwm2 +FAN_9 /sys/bus/i2c/devices/3-0023/hwmon/hwmon*/pwm3 +FAN_10 /sys/bus/i2c/devices/3-0023/hwmon/hwmon*/pwm4 +FAN_11 /sys/bus/i2c/devices/3-0023/hwmon/hwmon*/pwm5 +FAN_12 /sys/bus/i2c/devices/3-0023/hwmon/hwmon*/pwm6 +##### FAN GROUP END ##### + +##### INTERVAL (sec) ##### +INTERVAL=10 + +##### HYTERESIS (deg C) ##### +HYTERESIS=2 + +##### MINIMUM PWM (38.25=255x15%) ##### +MIN_PWM=39 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/funcs.sh b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/funcs.sh new file mode 100755 index 000000000000..3e417f699543 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/funcs.sh @@ -0,0 +1,22 @@ +#!/bin/bash +#/* +#********************************************************************** +#* +#* @filename funcs.sh +#* +#* @purpose api functions script for fan-ctrl +#* +#* @create 2017/08/09 +#* +#* @author nixon.chu +#* +#* @history 2017/08/09: init version +#* +#********************************************************************** +#*/ + +DIR=$(dirname $0) + +function Platform_init() { + ${DIR}/init.sh +} diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/init.sh b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/init.sh new file mode 100755 index 000000000000..06df071fa147 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/fan-ctrl/init.sh @@ -0,0 +1,26 @@ +#!/bin/bash +DIR=$(dirname $0) + +# board type +BOARD_TYPE=`cat /sys/bus/i2c/devices/1-0032/brd_type` + +# fan tables +FORWARD_SENSOR_CONF=${DIR}/fan-zone_F2B.conf +FORWARD_PWM_CONF=${DIR}/fan-zone-thermal_F2B.conf +REVERSE_SENSOR_CONF=${DIR}/fan-zone_B2F.conf +REVERSE_PWM_CONF=${DIR}/fan-zone-thermal_B2F.conf + +# Nixon: Please refer to "BMS CPLD Design Spec." +# board type : BRD_TYPE[3:0] +# 0x0 (0000b): BMS_AC_PSU_NORMAL_FAN, 0x2 (0010b): BMS_DC_PSU_NORMAL_FAN +# 0x1 (0001b): BMS_AC_PSU_REVERSE_FAN, 0x3 (0011b): BMS_DC_PSU_REVERSE_FAN +case $BOARD_TYPE in + 0x0|0x2) + ln -sf $FORWARD_SENSOR_CONF ${DIR}/fan-zone.conf + ln -sf $FORWARD_PWM_CONF ${DIR}/fan-zone-thermal.conf + ;; + 0x1|0x3) + ln -sf $REVERSE_SENSOR_CONF ${DIR}/fan-zone.conf + ln -sf $REVERSE_PWM_CONF ${DIR}/fan-zone-thermal.conf + ;; +esac diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/script/funcs.sh b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/script/funcs.sh new file mode 100755 index 000000000000..4b0c4c35471f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/script/funcs.sh @@ -0,0 +1,23 @@ +#/bin/bash + +# process name/id +DAEMON_NAME=`basename $0` +DAEMON_PID="$$" + +DEF_SEVERITY="INFO" + +#/* +#* FEATURE: +#* log_msg +#* PURPOSE: +#* log message +#* PARAMETERS: +#* msg (IN) message +#* RETURNS: +#* +#*/ +function log_msg() { + local msg=$1 + + `logger -t $DAEMON_NAME -p $DEF_SEVERITY $msg` +} diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/script/start_service.sh b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/script/start_service.sh new file mode 100755 index 000000000000..5049c0637be4 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/script/start_service.sh @@ -0,0 +1,162 @@ +#/bin/bash + +DIR=$(dirname $0) + +# include files +source ${DIR}/funcs.sh + +ACPI_INSTALL_PRINT=0 +ACPID_INSTALL_PRINT=0 +ACPI_SUPPORT_BASE_INSTALL_PRINT=0 +BC_INSTALL_PRINT=0 +ACPID_SOCKET_PRINT=0 +ACPI_GPE01_PRINT=0 +ACPI_GPE02_PRINT=0 +ACPI_GPE47_PRINT=0 +ACPI_STATUS=0 + +while [ 1 ] +do + if [ `dpkg -l |grep -c "acpi "` -eq "0" ]; then + dpkg -i /opt/debs/acpi_1.7-1_amd64.deb + if [ "$?" -ne "0" ]; then + if [ $ACPI_INSTALL_PRINT -eq 0 ]; then + ACPI_INSTALL_PRINT=1 + log_msg "Wait for acpi package install." + fi + sleep 1 + continue + else + log_msg "Install acpi package success." + fi + fi + break +done + +while [ 1 ] +do + if [ `dpkg -l |grep -c "acpid "` -eq "0" ]; then + dpkg -i /opt/debs/acpid_2.0.23-2_amd64.deb + if [ "$?" -ne "0" ]; then + if [ $ACPID_INSTALL_PRINT -eq 0 ]; then + ACPID_INSTALL_PRINT=1 + log_msg "Wait for acpid package install." + fi + sleep 1 + continue + else + log_msg "Install acpid package success." + fi + fi + break +done + +while [ 1 ] +do + if [ `dpkg -l |grep -c "acpi-support-base"` -eq "0" ]; then + dpkg -i /opt/debs/acpi-support-base_0.142-6_all.deb + if [ "$?" -ne "0" ]; then + if [ $ACPI_SUPPORT_BASE_INSTALL_PRINT -eq 0 ]; then + ACPI_SUPPORT_BASE_INSTALL_PRINT=1 + log_msg "Wait for acpi-support-base package install." + fi + sleep 1 + continue + else + log_msg "Install acpi-support-base package success." + fi + fi + break +done + +while [ 1 ] +do + if [ `dpkg -l |grep -c " bc "` -eq "0" ]; then + dpkg -i /opt/debs/bc_1.06.95-9_amd64.deb + if [ "$?" -ne "0" ]; then + if [ $BC_INSTALL_PRINT -eq 0 ]; then + BC_INSTALL_PRINT=1 + log_msg "Wait for bc package install." + fi + sleep 1 + continue + else + log_msg "Install bc package success." + fi + fi + break +done + +while [ 1 ] +do + if [ ! -e "/sys/firmware/acpi/interrupts/gpe01" ]; then + if [ $ACPI_GPE01_PRINT -eq 0 ]; then + ACPI_GPE01_PRINT=1 + log_msg "Wait for acpi gpe01 ready." + fi + sleep 1 + continue + fi + log_msg "The acpi gpe01 ready." + break +done + +while [ 1 ] +do + if [ ! -e "/sys/firmware/acpi/interrupts/gpe02" ]; then + if [ $ACPI_GPE02_PRINT -eq 0 ]; then + ACPI_GPE02_PRINT=1 + log_msg "Wait for acpi gpe02 ready." + fi + sleep 1 + continue + fi + log_msg "The acpi gpe02 ready." + break +done + +while [ 1 ] +do + if [ ! -e "/sys/firmware/acpi/interrupts/gpe47" ]; then + if [ $ACPI_GPE47_PRINT -eq 0 ]; then + ACPI_GPE47_PRINT=1 + log_msg "Wait for acpi gpe47 ready." + fi + sleep 1 + continue + fi + log_msg "The acpi gpe47 ready." + break +done + +while [ 1 ] +do + if [ ! -e "/var/run/acpid.socket" ]; then + if [ $ACPID_SOCKET_PRINT -eq 0 ]; then + ACPID_SOCKET_PRINT=1 + log_msg "Wait for acipd daemon start." + fi + sleep 1 + continue + fi + log_msg "The acpid daemon start." + break +done + +while [ 1 ] +do + if [ `/etc/init.d/acpid status | grep -c "active (running)"` -eq "0" ]; then + if [ $ACPI_STATUS -eq 0 ]; then + ACPI_STATUS=1 + log_msg "Wait for acpid running." + fi + sleep 1 + continue + fi + log_msg "The acpid running now." + break +done + +/etc/init.d/xcvr_servd start +/etc/init.d/sys_servd start +exit 0; diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/script/start_watchdog.sh b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/script/start_watchdog.sh new file mode 100755 index 000000000000..2268e5e0d16e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/script/start_watchdog.sh @@ -0,0 +1,32 @@ +#/bin/bash + +DIR=$(dirname $0) + +# include files +source ${DIR}/funcs.sh + +WATCHDOG_PRINT=0 + +while [ 1 ] +do + if [ `dpkg -l |grep -c "watchdog "` -eq "0" ]; then + dpkg -i /opt/debs/watchdog_5.14-3_amd64.deb + if [ "$?" -ne "0" ]; then + if [ $WATCHDOG_PRINT -eq 0 ]; then + WATCHDOG_PRINT=1 + log_msg "Wait for watchdog package install." + fi + sleep 1 + continue + else + log_msg "Install watchdog package success." + fi + fi + break +done + +ln -sf /opt/watchdog/watchdog.conf /etc/watchdog.conf + +/usr/sbin/watchdog -c /etc/watchdog.conf + +exit 0; diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/sys-serv/sys-servd b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/sys-serv/sys-servd new file mode 100755 index 000000000000..a1b4f177897c Binary files /dev/null and b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/sys-serv/sys-servd differ diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/system-check/system-check.sh b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/system-check/system-check.sh new file mode 100755 index 000000000000..f44ed046330d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/system-check/system-check.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +SW_READY_STAMP='/tmp/.BcmSdkReady' +SYSTEM_INIT=0 +CURRENTR_LED=0 + +# 0 was not ready, 1 was ready +SYSTEM_READY=1 + +function check_sdk_ready() { + if [ `file /var/run/docker-syncd/sswsyncd.socket | grep -c " socket"` -ne 1 ]; then + SYSTEM_READY=0 + return 1 + else + if [ `ss -a | grep "/var/run/sswsyncd/sswsyncd.socket" |grep -c "LISTEN"` -ne 1 ]; then + SYSTEM_READY=0 + return 1 + fi + fi + return 0 +} + +while [ true ] +do + SYSTEM_READY=1 + #-----check start------------ + check_sdk_ready + #-----cech end--------------- + + if [ "$SYSTEM_INIT" -eq "0" ]; then + if [ $SYSTEM_READY -eq 1 ]; then + SYSTEM_INIT=1 + # set SYSTEM LED to Green + echo 0x01 > /sys/bus/i2c/devices/1-0032/system_led_fld + CURRENTR_LED=1 + fi + else + if [ $SYSTEM_READY -eq 0 ]; then + # set SYSTEM LED to Amber + if [ "$CURRENTR_LED" -ne "2" ]; then + echo 0x02 > /sys/bus/i2c/devices/1-0032/system_led_fld + CURRENTR_LED=2 + fi + else + # set SYS LED to Green + if [ "$CURRENTR_LED" -ne "1" ]; then + echo 0x01 > /sys/bus/i2c/devices/1-0032/system_led_fld + CURRENTR_LED=1 + fi + fi + fi + sleep 10 +done + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/watchdog/watchdog.conf b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/watchdog/watchdog.conf new file mode 100644 index 000000000000..81f0b7b819bf --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/watchdog/watchdog.conf @@ -0,0 +1,45 @@ +#ping = 172.31.14.1 +#ping = 172.26.1.255 +#interface = eth0 +#file = /var/log/messages +#change = 1407 + +# Uncomment to enable test. Setting one of these values to '0' disables it. +# These values will hopefully never reboot your machine during normal use +# (if your machine is really hung, the loadavg will go much higher than 25) +#max-load-1 = 24 +#max-load-5 = 18 +#max-load-15 = 12 + +# Note that this is the number of pages! +# To get the real size, check how large the pagesize is on your machine. +#min-memory = 1 +#allocatable-memory = 1 + +#repair-binary = /usr/sbin/repair +#repair-timeout = +#test-binary = +#test-timeout = + +watchdog-device = /dev/watchdog + +# Defaults compiled into the binary +#temperature-device = +#max-temperature = 120 + +# Defaults compiled into the binary +#admin = root +#interval = 1 +#logtick = 1 +#log-dir = /var/log/watchdog + +# This greatly decreases the chance that watchdog won't be scheduled before +# your machine is really loaded +realtime = yes +priority = 99 + +# Check if rsyslogd is still running by enabling the following line +#pidfile = /var/run/rsyslogd.pid + +# timeout : 15 , 30 , 60 , 90 , 120 , 180 , 240 sec +watchdog-timeout = 120 diff --git a/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/xcvr-serv/xcvr-servd b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/xcvr-serv/xcvr-servd new file mode 100755 index 000000000000..c882ff8e335a Binary files /dev/null and b/platform/broadcom/sonic-platform-modules-mitac/ly1200-32x/opt/xcvr-serv/xcvr-servd differ diff --git a/platform/broadcom/sonic-platform-modules-quanta b/platform/broadcom/sonic-platform-modules-quanta deleted file mode 160000 index 2a300312a084..000000000000 --- a/platform/broadcom/sonic-platform-modules-quanta +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2a300312a084cd96cdd3657020b5e207adc04559 diff --git a/platform/broadcom/sonic-platform-modules-quanta/.gitignore b/platform/broadcom/sonic-platform-modules-quanta/.gitignore new file mode 100755 index 000000000000..2615f7f89bbb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/.gitignore @@ -0,0 +1,37 @@ +# Object files +*.o +*.ko +*.cmd +*.mod.c +*.symvers +*.order +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su diff --git a/platform/broadcom/sonic-platform-modules-quanta/LICENSE b/platform/broadcom/sonic-platform-modules-quanta/LICENSE new file mode 100755 index 000000000000..609b3082d11c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/LICENSE @@ -0,0 +1,16 @@ +Copyright (C) 2016 Microsoft, Inc +Copyright (C) 2018 Quanta Computer Inc. + +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. diff --git a/platform/broadcom/sonic-platform-modules-quanta/README.md b/platform/broadcom/sonic-platform-modules-quanta/README.md new file mode 100644 index 000000000000..bcc06271904a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/README.md @@ -0,0 +1,2 @@ +# sonic-platform-modules-quanta +SONiC platform folder which contain drivers for Quanta Switch diff --git a/platform/broadcom/sonic-platform-modules-quanta/debian/changelog b/platform/broadcom/sonic-platform-modules-quanta/debian/changelog new file mode 100755 index 000000000000..77d3407ec943 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/debian/changelog @@ -0,0 +1,6 @@ +sonic-quanta-platform-modules (1.0) unstable; urgency=low + + * Add support for Quanta IX1B-32X + * Initial release + + -- Chih-Pei Chang Jonathan Tsai Wed, 20 Dec 2017 09:26:01 +0800 diff --git a/platform/broadcom/sonic-platform-modules-quanta/debian/compat b/platform/broadcom/sonic-platform-modules-quanta/debian/compat new file mode 100755 index 000000000000..ec635144f600 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/debian/compat @@ -0,0 +1 @@ +9 diff --git a/platform/broadcom/sonic-platform-modules-quanta/debian/control b/platform/broadcom/sonic-platform-modules-quanta/debian/control new file mode 100755 index 000000000000..af058ee98f2d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/debian/control @@ -0,0 +1,12 @@ +Source: sonic-quanta-platform-modules +Section: main +Priority: extra +Maintainer: Chih-Pei Chang , Jonathan Tsai +Build-Depends: debhelper (>= 8.0.0), bzip2 +Standards-Version: 3.9.3 + +Package: sonic-platform-quanta-ix1b-32x +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as psu, led, sfp + diff --git a/platform/broadcom/sonic-platform-modules-quanta/debian/rules b/platform/broadcom/sonic-platform-modules-quanta/debian/rules new file mode 100755 index 000000000000..45bb29742a1c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/debian/rules @@ -0,0 +1,86 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +include /usr/share/dpkg/pkg-info.mk + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +export INSTALL_MOD_DIR:=extra + +PYTHON ?= python2 + +PACKAGE_PRE_NAME := sonic-platform-quanta +KVERSION ?= $(shell uname -r) +KERNEL_SRC := /lib/modules/$(KVERSION) +MOD_SRC_DIR:= $(shell pwd) +MODULE_DIRS:= ix1b-32x +MODULE_DIR := modules +UTILS_DIR := utils +SERVICE_DIR := service +CLASSES_DIR := classes +CONF_DIR := conf + +%: + dh $@ --with systemd,python2,python3 --buildsystem=pybuild + +clean: + dh_testdir + dh_testroot + dh_clean + +build: + #make modules -C $(KERNEL_SRC)/build M=$(MODULE_SRC) + (for mod in $(MODULE_DIRS); do \ + make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + $(PYTHON) $${mod}/setup.py build; \ + done) + +binary: binary-arch binary-indep + # Nothing to do + +binary-arch: + # Nothing to do + +#install: build + #dh_testdir + #dh_testroot + #dh_clean -k + #dh_installdirs + +binary-indep: + dh_testdir + dh_installdirs + + # Custom package commands + (for mod in $(MODULE_DIRS); do \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin; \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system; \ + cp $(MOD_SRC_DIR)/$${mod}/$(MODULE_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$${mod}/$(UTILS_DIR)/* debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin/; \ + cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/*.service debian/$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system/; \ + $(PYTHON) $${mod}/setup.py install --root=$(MOD_SRC_DIR)/debian/$(PACKAGE_PRE_NAME)-$${mod} --install-layout=deb; \ + done) + # Resuming debhelper scripts + dh_testroot + dh_install + dh_installchangelogs + dh_installdocs + dh_systemd_enable + dh_installinit + dh_systemd_start + dh_link + dh_fixperms + dh_compress + dh_strip + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb +.PHONY: build binary binary-arch binary-indep clean diff --git a/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/classes/__init__.py b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/classes/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/Makefile b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/Makefile new file mode 100755 index 000000000000..fd73f274ef53 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/Makefile @@ -0,0 +1,3 @@ +obj-m:=qci_pmbus.o qci_cpld_qsfp28.o qci_platform_ix1b.o + + diff --git a/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/pmbus.h b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/pmbus.h new file mode 100755 index 000000000000..fa9beb3eb60c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/pmbus.h @@ -0,0 +1,387 @@ +/* + * pmbus.h - Common defines and structures for PMBus devices + * + * Copyright (c) 2010, 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PMBUS_H +#define PMBUS_H + +/* + * Registers + */ +#define PMBUS_PAGE 0x00 +#define PMBUS_OPERATION 0x01 +#define PMBUS_ON_OFF_CONFIG 0x02 +#define PMBUS_CLEAR_FAULTS 0x03 +#define PMBUS_PHASE 0x04 + +#define PMBUS_CAPABILITY 0x19 +#define PMBUS_QUERY 0x1A + +#define PMBUS_VOUT_MODE 0x20 +#define PMBUS_VOUT_COMMAND 0x21 +#define PMBUS_VOUT_TRIM 0x22 +#define PMBUS_VOUT_CAL_OFFSET 0x23 +#define PMBUS_VOUT_MAX 0x24 +#define PMBUS_VOUT_MARGIN_HIGH 0x25 +#define PMBUS_VOUT_MARGIN_LOW 0x26 +#define PMBUS_VOUT_TRANSITION_RATE 0x27 +#define PMBUS_VOUT_DROOP 0x28 +#define PMBUS_VOUT_SCALE_LOOP 0x29 +#define PMBUS_VOUT_SCALE_MONITOR 0x2A + +#define PMBUS_COEFFICIENTS 0x30 +#define PMBUS_POUT_MAX 0x31 + +#define PMBUS_FAN_CONFIG_12 0x3A +#define PMBUS_FAN_COMMAND_1 0x3B +#define PMBUS_FAN_COMMAND_2 0x3C +#define PMBUS_FAN_CONFIG_34 0x3D +#define PMBUS_FAN_COMMAND_3 0x3E +#define PMBUS_FAN_COMMAND_4 0x3F + +#define PMBUS_VOUT_OV_FAULT_LIMIT 0x40 +#define PMBUS_VOUT_OV_FAULT_RESPONSE 0x41 +#define PMBUS_VOUT_OV_WARN_LIMIT 0x42 +#define PMBUS_VOUT_UV_WARN_LIMIT 0x43 +#define PMBUS_VOUT_UV_FAULT_LIMIT 0x44 +#define PMBUS_VOUT_UV_FAULT_RESPONSE 0x45 +#define PMBUS_IOUT_OC_FAULT_LIMIT 0x46 +#define PMBUS_IOUT_OC_FAULT_RESPONSE 0x47 +#define PMBUS_IOUT_OC_LV_FAULT_LIMIT 0x48 +#define PMBUS_IOUT_OC_LV_FAULT_RESPONSE 0x49 +#define PMBUS_IOUT_OC_WARN_LIMIT 0x4A +#define PMBUS_IOUT_UC_FAULT_LIMIT 0x4B +#define PMBUS_IOUT_UC_FAULT_RESPONSE 0x4C + +#define PMBUS_OT_FAULT_LIMIT 0x4F +#define PMBUS_OT_FAULT_RESPONSE 0x50 +#define PMBUS_OT_WARN_LIMIT 0x51 +#define PMBUS_UT_WARN_LIMIT 0x52 +#define PMBUS_UT_FAULT_LIMIT 0x53 +#define PMBUS_UT_FAULT_RESPONSE 0x54 +#define PMBUS_VIN_OV_FAULT_LIMIT 0x55 +#define PMBUS_VIN_OV_FAULT_RESPONSE 0x56 +#define PMBUS_VIN_OV_WARN_LIMIT 0x57 +#define PMBUS_VIN_UV_WARN_LIMIT 0x58 +#define PMBUS_VIN_UV_FAULT_LIMIT 0x59 + +#define PMBUS_IIN_OC_FAULT_LIMIT 0x5B +#define PMBUS_IIN_OC_WARN_LIMIT 0x5D + +#define PMBUS_POUT_OP_FAULT_LIMIT 0x68 +#define PMBUS_POUT_OP_WARN_LIMIT 0x6A +#define PMBUS_PIN_OP_WARN_LIMIT 0x6B + +#define PMBUS_STATUS_BYTE 0x78 +#define PMBUS_STATUS_WORD 0x79 +#define PMBUS_STATUS_VOUT 0x7A +#define PMBUS_STATUS_IOUT 0x7B +#define PMBUS_STATUS_INPUT 0x7C +#define PMBUS_STATUS_TEMPERATURE 0x7D +#define PMBUS_STATUS_CML 0x7E +#define PMBUS_STATUS_OTHER 0x7F +#define PMBUS_STATUS_MFR_SPECIFIC 0x80 +#define PMBUS_STATUS_FAN_12 0x81 +#define PMBUS_STATUS_FAN_34 0x82 + +#define PMBUS_READ_VIN 0x88 +#define PMBUS_READ_IIN 0x89 +#define PMBUS_READ_VCAP 0x8A +#define PMBUS_READ_VOUT 0x8B +#define PMBUS_READ_IOUT 0x8C +#define PMBUS_READ_TEMPERATURE_1 0x8D +#define PMBUS_READ_TEMPERATURE_2 0x8E +#define PMBUS_READ_TEMPERATURE_3 0x8F +#define PMBUS_READ_FAN_SPEED_1 0x90 +#define PMBUS_READ_FAN_SPEED_2 0x91 +#define PMBUS_READ_FAN_SPEED_3 0x92 +#define PMBUS_READ_FAN_SPEED_4 0x93 +#define PMBUS_READ_DUTY_CYCLE 0x94 +#define PMBUS_READ_FREQUENCY 0x95 +#define PMBUS_READ_POUT 0x96 +#define PMBUS_READ_PIN 0x97 + +#define PMBUS_REVISION 0x98 +#define PMBUS_MFR_ID 0x99 +#define PMBUS_MFR_MODEL 0x9A +#define PMBUS_MFR_REVISION 0x9B +#define PMBUS_MFR_LOCATION 0x9C +#define PMBUS_MFR_DATE 0x9D +#define PMBUS_MFR_SERIAL 0x9E + +/* + * Virtual registers. + * Useful to support attributes which are not supported by standard PMBus + * registers but exist as manufacturer specific registers on individual chips. + * Must be mapped to real registers in device specific code. + * + * Semantics: + * Virtual registers are all word size. + * READ registers are read-only; writes are either ignored or return an error. + * RESET registers are read/write. Reading reset registers returns zero + * (used for detection), writing any value causes the associated history to be + * reset. + * Virtual registers have to be handled in device specific driver code. Chip + * driver code returns non-negative register values if a virtual register is + * supported, or a negative error code if not. The chip driver may return + * -ENODATA or any other error code in this case, though an error code other + * than -ENODATA is handled more efficiently and thus preferred. Either case, + * the calling PMBus core code will abort if the chip driver returns an error + * code when reading or writing virtual registers. + */ +#define PMBUS_VIRT_BASE 0x100 +#define PMBUS_VIRT_READ_TEMP_AVG (PMBUS_VIRT_BASE + 0) +#define PMBUS_VIRT_READ_TEMP_MIN (PMBUS_VIRT_BASE + 1) +#define PMBUS_VIRT_READ_TEMP_MAX (PMBUS_VIRT_BASE + 2) +#define PMBUS_VIRT_RESET_TEMP_HISTORY (PMBUS_VIRT_BASE + 3) +#define PMBUS_VIRT_READ_VIN_AVG (PMBUS_VIRT_BASE + 4) +#define PMBUS_VIRT_READ_VIN_MIN (PMBUS_VIRT_BASE + 5) +#define PMBUS_VIRT_READ_VIN_MAX (PMBUS_VIRT_BASE + 6) +#define PMBUS_VIRT_RESET_VIN_HISTORY (PMBUS_VIRT_BASE + 7) +#define PMBUS_VIRT_READ_IIN_AVG (PMBUS_VIRT_BASE + 8) +#define PMBUS_VIRT_READ_IIN_MIN (PMBUS_VIRT_BASE + 9) +#define PMBUS_VIRT_READ_IIN_MAX (PMBUS_VIRT_BASE + 10) +#define PMBUS_VIRT_RESET_IIN_HISTORY (PMBUS_VIRT_BASE + 11) +#define PMBUS_VIRT_READ_PIN_AVG (PMBUS_VIRT_BASE + 12) +#define PMBUS_VIRT_READ_PIN_MAX (PMBUS_VIRT_BASE + 13) +#define PMBUS_VIRT_RESET_PIN_HISTORY (PMBUS_VIRT_BASE + 14) +#define PMBUS_VIRT_READ_POUT_AVG (PMBUS_VIRT_BASE + 15) +#define PMBUS_VIRT_READ_POUT_MAX (PMBUS_VIRT_BASE + 16) +#define PMBUS_VIRT_RESET_POUT_HISTORY (PMBUS_VIRT_BASE + 17) +#define PMBUS_VIRT_READ_VOUT_AVG (PMBUS_VIRT_BASE + 18) +#define PMBUS_VIRT_READ_VOUT_MIN (PMBUS_VIRT_BASE + 19) +#define PMBUS_VIRT_READ_VOUT_MAX (PMBUS_VIRT_BASE + 20) +#define PMBUS_VIRT_RESET_VOUT_HISTORY (PMBUS_VIRT_BASE + 21) +#define PMBUS_VIRT_READ_IOUT_AVG (PMBUS_VIRT_BASE + 22) +#define PMBUS_VIRT_READ_IOUT_MIN (PMBUS_VIRT_BASE + 23) +#define PMBUS_VIRT_READ_IOUT_MAX (PMBUS_VIRT_BASE + 24) +#define PMBUS_VIRT_RESET_IOUT_HISTORY (PMBUS_VIRT_BASE + 25) +#define PMBUS_VIRT_READ_TEMP2_AVG (PMBUS_VIRT_BASE + 26) +#define PMBUS_VIRT_READ_TEMP2_MIN (PMBUS_VIRT_BASE + 27) +#define PMBUS_VIRT_READ_TEMP2_MAX (PMBUS_VIRT_BASE + 28) +#define PMBUS_VIRT_RESET_TEMP2_HISTORY (PMBUS_VIRT_BASE + 29) + +#define PMBUS_VIRT_READ_VMON (PMBUS_VIRT_BASE + 30) +#define PMBUS_VIRT_VMON_UV_WARN_LIMIT (PMBUS_VIRT_BASE + 31) +#define PMBUS_VIRT_VMON_OV_WARN_LIMIT (PMBUS_VIRT_BASE + 32) +#define PMBUS_VIRT_VMON_UV_FAULT_LIMIT (PMBUS_VIRT_BASE + 33) +#define PMBUS_VIRT_VMON_OV_FAULT_LIMIT (PMBUS_VIRT_BASE + 34) +#define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35) + +/* + * CAPABILITY + */ +#define PB_CAPABILITY_SMBALERT (1<<4) +#define PB_CAPABILITY_ERROR_CHECK (1<<7) + +/* + * VOUT_MODE + */ +#define PB_VOUT_MODE_MODE_MASK 0xe0 +#define PB_VOUT_MODE_PARAM_MASK 0x1f + +#define PB_VOUT_MODE_LINEAR 0x00 +#define PB_VOUT_MODE_VID 0x20 +#define PB_VOUT_MODE_DIRECT 0x40 + +/* + * Fan configuration + */ +#define PB_FAN_2_PULSE_MASK ((1 << 0) | (1 << 1)) +#define PB_FAN_2_RPM (1 << 2) +#define PB_FAN_2_INSTALLED (1 << 3) +#define PB_FAN_1_PULSE_MASK ((1 << 4) | (1 << 5)) +#define PB_FAN_1_RPM (1 << 6) +#define PB_FAN_1_INSTALLED (1 << 7) + +/* + * STATUS_BYTE, STATUS_WORD (lower) + */ +#define PB_STATUS_NONE_ABOVE (1<<0) +#define PB_STATUS_CML (1<<1) +#define PB_STATUS_TEMPERATURE (1<<2) +#define PB_STATUS_VIN_UV (1<<3) +#define PB_STATUS_IOUT_OC (1<<4) +#define PB_STATUS_VOUT_OV (1<<5) +#define PB_STATUS_OFF (1<<6) +#define PB_STATUS_BUSY (1<<7) + +/* + * STATUS_WORD (upper) + */ +#define PB_STATUS_UNKNOWN (1<<8) +#define PB_STATUS_OTHER (1<<9) +#define PB_STATUS_FANS (1<<10) +#define PB_STATUS_POWER_GOOD_N (1<<11) +#define PB_STATUS_WORD_MFR (1<<12) +#define PB_STATUS_INPUT (1<<13) +#define PB_STATUS_IOUT_POUT (1<<14) +#define PB_STATUS_VOUT (1<<15) + +/* + * STATUS_IOUT + */ +#define PB_POUT_OP_WARNING (1<<0) +#define PB_POUT_OP_FAULT (1<<1) +#define PB_POWER_LIMITING (1<<2) +#define PB_CURRENT_SHARE_FAULT (1<<3) +#define PB_IOUT_UC_FAULT (1<<4) +#define PB_IOUT_OC_WARNING (1<<5) +#define PB_IOUT_OC_LV_FAULT (1<<6) +#define PB_IOUT_OC_FAULT (1<<7) + +/* + * STATUS_VOUT, STATUS_INPUT + */ +#define PB_VOLTAGE_UV_FAULT (1<<4) +#define PB_VOLTAGE_UV_WARNING (1<<5) +#define PB_VOLTAGE_OV_WARNING (1<<6) +#define PB_VOLTAGE_OV_FAULT (1<<7) + +/* + * STATUS_INPUT + */ +#define PB_PIN_OP_WARNING (1<<0) +#define PB_IIN_OC_WARNING (1<<1) +#define PB_IIN_OC_FAULT (1<<2) + +/* + * STATUS_TEMPERATURE + */ +#define PB_TEMP_UT_FAULT (1<<4) +#define PB_TEMP_UT_WARNING (1<<5) +#define PB_TEMP_OT_WARNING (1<<6) +#define PB_TEMP_OT_FAULT (1<<7) + +/* + * STATUS_FAN + */ +#define PB_FAN_AIRFLOW_WARNING (1<<0) +#define PB_FAN_AIRFLOW_FAULT (1<<1) +#define PB_FAN_FAN2_SPEED_OVERRIDE (1<<2) +#define PB_FAN_FAN1_SPEED_OVERRIDE (1<<3) +#define PB_FAN_FAN2_WARNING (1<<4) +#define PB_FAN_FAN1_WARNING (1<<5) +#define PB_FAN_FAN2_FAULT (1<<6) +#define PB_FAN_FAN1_FAULT (1<<7) + +/* + * CML_FAULT_STATUS + */ +#define PB_CML_FAULT_OTHER_MEM_LOGIC (1<<0) +#define PB_CML_FAULT_OTHER_COMM (1<<1) +#define PB_CML_FAULT_PROCESSOR (1<<3) +#define PB_CML_FAULT_MEMORY (1<<4) +#define PB_CML_FAULT_PACKET_ERROR (1<<5) +#define PB_CML_FAULT_INVALID_DATA (1<<6) +#define PB_CML_FAULT_INVALID_COMMAND (1<<7) + +enum pmbus_sensor_classes { + PSC_VOLTAGE_IN = 0, + PSC_VOLTAGE_OUT, + PSC_CURRENT_IN, + PSC_CURRENT_OUT, + PSC_POWER, + PSC_TEMPERATURE, + PSC_FAN, + PSC_NUM_CLASSES /* Number of power sensor classes */ +}; + +#define PMBUS_PAGES 32 /* Per PMBus specification */ + +/* Functionality bit mask */ +#define PMBUS_HAVE_VIN (1 << 0) +#define PMBUS_HAVE_VCAP (1 << 1) +#define PMBUS_HAVE_VOUT (1 << 2) +#define PMBUS_HAVE_IIN (1 << 3) +#define PMBUS_HAVE_IOUT (1 << 4) +#define PMBUS_HAVE_PIN (1 << 5) +#define PMBUS_HAVE_POUT (1 << 6) +#define PMBUS_HAVE_FAN12 (1 << 7) +#define PMBUS_HAVE_FAN34 (1 << 8) +#define PMBUS_HAVE_TEMP (1 << 9) +#define PMBUS_HAVE_TEMP2 (1 << 10) +#define PMBUS_HAVE_TEMP3 (1 << 11) +#define PMBUS_HAVE_STATUS_VOUT (1 << 12) +#define PMBUS_HAVE_STATUS_IOUT (1 << 13) +#define PMBUS_HAVE_STATUS_INPUT (1 << 14) +#define PMBUS_HAVE_STATUS_TEMP (1 << 15) +#define PMBUS_HAVE_STATUS_FAN12 (1 << 16) +#define PMBUS_HAVE_STATUS_FAN34 (1 << 17) +#define PMBUS_HAVE_VMON (1 << 18) +#define PMBUS_HAVE_STATUS_VMON (1 << 19) + +enum pmbus_data_format { linear = 0, direct, vid }; + +struct pmbus_driver_info { + int pages; /* Total number of pages */ + enum pmbus_data_format format[PSC_NUM_CLASSES]; + /* + * Support one set of coefficients for each sensor type + * Used for chips providing data in direct mode. + */ + int m[PSC_NUM_CLASSES]; /* mantissa for direct data format */ + int b[PSC_NUM_CLASSES]; /* offset */ + int R[PSC_NUM_CLASSES]; /* exponent */ + + u32 func[PMBUS_PAGES]; /* Functionality, per page */ + /* + * The following functions map manufacturing specific register values + * to PMBus standard register values. Specify only if mapping is + * necessary. + * Functions return the register value (read) or zero (write) if + * successful. A return value of -ENODATA indicates that there is no + * manufacturer specific register, but that a standard PMBus register + * may exist. Any other negative return value indicates that the + * register does not exist, and that no attempt should be made to read + * the standard register. + */ + int (*read_byte_data)(struct i2c_client *client, int page, int reg); + int (*read_word_data)(struct i2c_client *client, int page, int reg); + int (*write_word_data)(struct i2c_client *client, int page, int reg, + u16 word); + int (*write_byte)(struct i2c_client *client, int page, u8 value); + /* + * The identify function determines supported PMBus functionality. + * This function is only necessary if a chip driver supports multiple + * chips, and the chip functionality is not pre-determined. + */ + int (*identify)(struct i2c_client *client, + struct pmbus_driver_info *info); +}; + +/* Function declarations */ + +void pmbus_clear_cache(struct i2c_client *client); +int pmbus_set_page(struct i2c_client *client, u8 page); +int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); +int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word); +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); +int pmbus_write_byte(struct i2c_client *client, int page, u8 value); +void pmbus_clear_faults(struct i2c_client *client); +bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); +bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); +int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, + struct pmbus_driver_info *info); +int pmbus_do_remove(struct i2c_client *client); +const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client + *client); + +#endif /* PMBUS_H */ diff --git a/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/qci_cpld_qsfp28.c b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/qci_cpld_qsfp28.c new file mode 100755 index 000000000000..15c5369fd219 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/qci_cpld_qsfp28.c @@ -0,0 +1,409 @@ +/* + * A CPLD driver for monitor QSFP28 module I/O + * + * The CPLD is customize by Quanta for controlling QSFP28 module signals, + * they are RESET , INTERREPT , Module_Present, LPMODE + * Each CPLD control 16 modules, each module use 4 bits in register. + * + * Copyright (C) 2015 Quanta Inc. + * + * Author: Luffy Cheng + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_IDA(cpld_ida); + +enum platform_type { + QSFP28 = 0, + NONE +}; + +static struct class *cpld_class = NULL; + +struct sfp_data { + struct i2c_client *cpld_client; + char name[8]; + u8 port_id; + u8 cpld_port; +}; + +struct cpld_data { + struct mutex lock; + struct device *port_dev[16]; + struct sfp_data *port_data[16]; +}; + +static int cpld_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int cpld_remove(struct i2c_client *client); + +static const struct i2c_device_id cpld_id[] = { + { "CPLD-QSFP28", QSFP28 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cpld_id); + +static struct i2c_driver cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "qci_cpld_qsfp28", + }, + .probe = cpld_probe, + .remove = cpld_remove, + .id_table = cpld_id, +// .address_list = normal_i2c, +}; + +#define CPLD_ID_PREFIX "port-" +#define CPLD_ID_FORMAT CPLD_ID_PREFIX "%d" + +#define RESET_MASK 0x08 +#define INTERRUPT_MASK 0x04 +#define MODULE_PRESENT_MASK 0x02 +#define LPMODE_MASK 0x01 + +static inline u8 get_group_cmd(u8 group) +{ + //FIXME: if group cmd change + return (group + 1); +} + +static inline u8 port_remapping(u8 phy_port) +{ + /* FIXME: implement by hardware design */ + /* The CPLD register port mapping is weird : + * MSB -------- LSB (word data) + * P3 P4 P1 P2 (per port 4 bits) + * For easy coding bit shift, we treat it as hw port swap + */ + return (phy_port % 2) ? (phy_port - 1) : (phy_port + 1); +} + +static ssize_t get_reset(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sfp_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->cpld_client; + u8 group = (u8)(data->cpld_port / 4); + u8 group_port = data->cpld_port % 4; + s32 value; + + dev_dbg(&client->dev, "port_id %d => cpld_port %d, group %d(%d)\n", data->port_id, + data->cpld_port + 1, group + 1, group_port + 1); + + value = i2c_smbus_read_word_data(client, get_group_cmd(group)); + if (value < 0) + return -ENODEV; + + dev_dbg(&client->dev, "read group%d value= %x\n", group + 1, value); + + value >>= (group_port * 4); + value &= RESET_MASK; + + return sprintf(buf, "%d\n", value ? 1 : 0); +} + +static ssize_t get_interrupt(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sfp_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->cpld_client; + u8 group = (u8)(data->cpld_port / 4); + u8 group_port = data->cpld_port % 4; + s32 value; + + dev_dbg(&client->dev, "port_id %d => cpld_port %d, group %d(%d)\n", data->port_id, + data->cpld_port + 1, group + 1, group_port + 1); + + value = i2c_smbus_read_word_data(client, get_group_cmd(group)); + if (value < 0) + return -ENODEV; + + dev_dbg(&client->dev, "read group%d value= %x\n", group + 1, value); + + value >>= (group_port * 4); + value &= INTERRUPT_MASK; + + return sprintf(buf, "%d\n", value ? 1 : 0); +} + +static ssize_t get_module_present(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sfp_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->cpld_client; + u8 group = (u8)(data->cpld_port / 4); + u8 group_port = data->cpld_port % 4; + s32 value; + + dev_dbg(&client->dev, "port_id %d => cpld_port %d, group %d(%d)\n", data->port_id, + data->cpld_port + 1, group + 1, group_port + 1); + + value = i2c_smbus_read_word_data(client, get_group_cmd(group)); + if (value < 0) + return -ENODEV; + + dev_dbg(&client->dev, "read group%d value= %x\n", group + 1, value); + + value >>= (group_port * 4); + value &= MODULE_PRESENT_MASK; + + //FIXME: if present is not low active + return sprintf(buf, "%d\n", value ? 0 : 1); +} + +static ssize_t get_lpmode(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sfp_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->cpld_client; + u8 group = (u8)(data->cpld_port / 4); + u8 group_port = data->cpld_port % 4; + s32 value; + + dev_dbg(&client->dev, "port_id %d => cpld_port %d, group %d(%d)\n", data->port_id, + data->cpld_port + 1, group + 1, group_port + 1); + + value = i2c_smbus_read_word_data(client, get_group_cmd(group)); + if (value < 0) + return -ENODEV; + + dev_dbg(&client->dev, "read group%d value= %x\n", group + 1, value); + + value >>= (group_port * 4); + value &= LPMODE_MASK; + + return sprintf(buf, "%d\n", value ? 1 : 0); +} + +static ssize_t set_reset(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sfp_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->cpld_client; + u8 group = (u8)(data->cpld_port / 4); + u8 group_port = data->cpld_port % 4; + s32 value; + long disable; + + dev_dbg(&client->dev, "port_id %d => cpld_port %d, group %d(%d)\n", data->port_id, + data->cpld_port + 1, group + 1, group_port + 1); + + if (kstrtol(buf, 0, &disable)) + return -EINVAL; + + if ((disable != 1) && (disable != 0)) + return -EINVAL; + +// mutex_lock(&data->lock); + value = i2c_smbus_read_word_data(client, get_group_cmd(group)); + if (value < 0) + return -ENODEV; + + dev_dbg(&client->dev, "read group%d value= %x\n", group + 1, value); + + value &= ~(RESET_MASK << (group_port * 4)); + if (disable) + value |= (RESET_MASK << (group_port * 4)); + + dev_dbg(&client->dev, "write group%d value= %x\n", group + 1, value); + + i2c_smbus_write_word_data(client, get_group_cmd(group), (u16)value); +// mutex_unlock(&data->lock); + + return count; +} + +static ssize_t set_lpmode(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sfp_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->cpld_client; + u8 group = (u8)(data->cpld_port / 4); + u8 group_port = data->cpld_port % 4; + s32 value; + long disable; + + dev_dbg(&client->dev, "port_id %d => cpld_port %d, group %d(%d)\n", data->port_id, + data->cpld_port + 1, group + 1, group_port + 1); + + if (kstrtol(buf, 0, &disable)) + return -EINVAL; + + if ((disable != 1) && (disable != 0)) + return -EINVAL; + +// mutex_lock(&data->lock); + value = i2c_smbus_read_word_data(client, get_group_cmd(group)); + if (value < 0) + return -ENODEV; + + dev_dbg(&client->dev, "read group%d value= %x\n", group + 1, value); + + value &= ~(LPMODE_MASK << (group_port * 4)); + if (disable) + value |= (LPMODE_MASK << (group_port * 4)); + + dev_dbg(&client->dev, "write group%d value= %x\n", group + 1, value); + + i2c_smbus_write_word_data(client, get_group_cmd(group), (u16)value); +// mutex_unlock(&data->lock); + + return count; +} + +static DEVICE_ATTR(reset, S_IWUSR | S_IRUGO, get_reset, set_reset); +static DEVICE_ATTR(lpmode, S_IWUSR | S_IRUGO, get_lpmode, set_lpmode); +static DEVICE_ATTR(module_present, S_IRUGO, get_module_present, NULL); +static DEVICE_ATTR(interrupt, S_IRUGO, get_interrupt, NULL); + +static const struct attribute *sfp_attrs[] = { + &dev_attr_reset.attr, + &dev_attr_lpmode.attr, + &dev_attr_module_present.attr, + &dev_attr_interrupt.attr, + NULL, +}; + +static const struct attribute_group sfp_attr_group = { + .attrs = (struct attribute **) sfp_attrs, +}; + +static int cpld_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cpld_data *data; + struct sfp_data *port_data; + struct device *port_dev; + int port_nr, i, err; + + if (!cpld_class) + { + cpld_class = class_create(THIS_MODULE, "cpld-qsfp28"); + if (IS_ERR(cpld_class)) { + pr_err("couldn't create sysfs class\n"); + return PTR_ERR(cpld_class); + } + } + + data = devm_kzalloc(&client->dev, sizeof(struct cpld_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* register sfp port data to sysfs */ + for (i = 0; i < 16; i++) + { + port_nr = ida_simple_get(&cpld_ida, 1, 99, GFP_KERNEL); + if (port_nr < 0) + return ERR_PTR(port_nr); + + port_data = kzalloc(sizeof(struct sfp_data), GFP_KERNEL); + + port_dev = device_create(cpld_class, &client->dev, MKDEV(0,0), port_data, CPLD_ID_FORMAT, port_nr); + if (IS_ERR(port_dev)) { + err = PTR_ERR(port_dev); + // printk("err_status\n"); + } + + data->port_dev[i] = port_dev; + data->port_data[i] = port_data; + + dev_info(&client->dev, "Register qsfp28 port-%d\n", port_nr); + + /* FIXME: implement Logical/Physical port remapping */ + //port_data->cpld_port = i; + port_data->cpld_port = port_remapping(i); + sprintf(port_data->name, "port-%d", port_nr); + port_data->port_id = port_nr; + dev_set_drvdata(port_dev, port_data); + port_dev->init_name = port_data->name; + port_data->cpld_client = client; + + err = sysfs_create_group(&port_dev->kobj, &sfp_attr_group); + // if (status) printk("err status\n"); + } + + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + + dev_info(&client->dev, "%s device found\n", client->name); + + + return 0; + +//FIXME: implement error check +exit_remove: +// sysfs_remove_group(&client->dev.kobj, &data->attrs); + return err; +} + +/* FIXME: for older kernel doesn't with idr_is_empty function, implement here */ +static int idr_has_entry(int id, void *p, void *data) +{ + return 1; +} + +static bool cpld_idr_is_empty(struct idr *idp) +{ + return !idr_for_each(idp, idr_has_entry, NULL); +} + +static int cpld_remove(struct i2c_client *client) +{ + struct cpld_data *data = i2c_get_clientdata(client); + int i, id; + + for (i = 15; i >= 0; i--) + { + dev_info(data->port_dev[i], "Remove qsfp28 port-%d\n", data->port_data[i]->port_id); + device_unregister(data->port_dev[i]); + ida_simple_remove(&cpld_ida, data->port_data[i]->port_id); + kfree(data->port_data[i]); + } + + if (cpld_idr_is_empty(&cpld_ida.idr)) + class_destroy(cpld_class); + + return 0; +} + +module_i2c_driver(cpld_driver); + +MODULE_AUTHOR("Luffy Cheng "); +MODULE_DESCRIPTION("Quanta Switch SFP CPLD driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/qci_platform.h b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/qci_platform.h new file mode 100755 index 000000000000..0737c8ce8170 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/qci_platform.h @@ -0,0 +1,64 @@ +/* + * Driver model definations for Quanta Platform drivers + * + * Copyright (C) 2015-2016 Quanta QCT + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#ifndef __QCI_PLATFORM_H_INCLUDED +#define __QCI_PLATFORM_H_INCLUDED + +#define MUX_INFO(bus, deselect) \ + {.adap_id = bus, .deselect_on_exit = deselect} + +#define GPIO_INFO(id, gpio_nr) \ + {.gpio_id = id, .system_gpio_nr = gpio_nr} + +struct platform_gpio { + int gpio_id; + int system_gpio_nr; +}; + +/* FIXME: Please add important GPIO which need to be request */ +enum PLATFORM_GPIO_ID { + GPIO_PSU1_PRSNT, + GPIO_PSU1_PWRGD, + GPIO_PSU2_PRSNT, + GPIO_PSU2_PWRGD, +}; + +#define LED_ON LED_FULL + +#define LED_INFO(id) \ + {.led_id = id, .cdev = NULL} + +struct platform_led { + int led_id; + struct led_classdev *cdev; +}; + +enum PLATFORM_LED_ID { + SYSLED_AMBER, + SYSLED_GREEN, + FRONT_PSU1_GREEN, + FRONT_PSU1_RED, + FRONT_PSU2_GREEN, + FRONT_PSU2_RED, + FRONT_FAN_GREEN, + FRONT_FAN_RED, + REAR_FAN1_RED, + REAR_FAN2_RED, + REAR_FAN3_RED, + REAR_FAN4_RED, + REAR_FAN5_RED, + REAR_FAN6_RED, + TOTAL_LED_NR +}; + +extern int qci_platform_get_gpio(int platform_gpio); +extern int qci_platform_set_led(int led_id, bool led_on); + +#endif /* __QCI_PLATFORM_H_INCLUDED */ diff --git a/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/qci_platform_ix1b.c b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/qci_platform_ix1b.c new file mode 100755 index 000000000000..f783c88fb9a1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/qci_platform_ix1b.c @@ -0,0 +1,491 @@ +/* + * Quanta IX1B platform driver + * + * + * Copyright (C) 2014 Quanta Computer inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE > KERNEL_VERSION(3,12,0) +#include +#else +#include +#endif +#include "qci_platform.h" + +#define QUANTA_CPU_RGL + +static struct pca954x_platform_mode pca9548sfp1_modes[] = { + MUX_INFO(0x20, 1), + MUX_INFO(0x21, 1), + MUX_INFO(0x22, 1), + MUX_INFO(0x23, 1), + MUX_INFO(0x24, 1), + MUX_INFO(0x25, 1), + MUX_INFO(0x26, 1), + MUX_INFO(0x27, 1), +}; + +static struct pca954x_platform_data pca9548sfp1_data = { + .modes = pca9548sfp1_modes, + .num_modes = 8, +}; + +static struct pca954x_platform_mode pca9548sfp2_modes[] = { + MUX_INFO(0x28, 1), + MUX_INFO(0x29, 1), + MUX_INFO(0x2a, 1), + MUX_INFO(0x2b, 1), + MUX_INFO(0x2c, 1), + MUX_INFO(0x2d, 1), + MUX_INFO(0x2e, 1), + MUX_INFO(0x2f, 1), +}; + +static struct pca954x_platform_data pca9548sfp2_data = { + .modes = pca9548sfp2_modes, + .num_modes = 8, +}; + +static struct pca954x_platform_mode pca9548sfp3_modes[] = { + MUX_INFO(0x30, 1), + MUX_INFO(0x31, 1), + MUX_INFO(0x32, 1), + MUX_INFO(0x33, 1), + MUX_INFO(0x34, 1), + MUX_INFO(0x35, 1), + MUX_INFO(0x36, 1), + MUX_INFO(0x37, 1), +}; + +static struct pca954x_platform_data pca9548sfp3_data = { + .modes = pca9548sfp3_modes, + .num_modes = 8, +}; + +static struct pca954x_platform_mode pca9548sfp4_modes[] = { + MUX_INFO(0x38, 1), + MUX_INFO(0x39, 1), + MUX_INFO(0x3a, 1), + MUX_INFO(0x3b, 1), + MUX_INFO(0x3c, 1), + MUX_INFO(0x3d, 1), + MUX_INFO(0x3e, 1), + MUX_INFO(0x3f, 1), +}; + +static struct pca954x_platform_data pca9548sfp4_data = { + .modes = pca9548sfp4_modes, + .num_modes = 8, +}; + +static struct pca954x_platform_mode pca9546_1_modes[] = { + MUX_INFO(0x10, 1), + MUX_INFO(0x11, 1), + MUX_INFO(0x12, 1), + MUX_INFO(0x13, 1), +}; + +static struct pca954x_platform_data pca9546_1_data = { + .modes = pca9546_1_modes, + .num_modes = 4, +}; + +static struct pca954x_platform_mode pca9546_2_modes[] = { + MUX_INFO(0x14, 1), + MUX_INFO(0x15, 1), + MUX_INFO(0x16, 1), + MUX_INFO(0x17, 1), +}; + +static struct pca954x_platform_data pca9546_2_data = { + .modes = pca9546_2_modes, + .num_modes = 4, +}; + +/* CPU gpio base defined in devicetree + * default is ARCH_NR_GPIOS - 32, (256-32=224) + * It must match devicetree default gpio base + * */ +#define PCA9555_PSU_GPIO_BASE 0x10 +#define PCA9555_FAN_GPIO_BASE 0x20 +#define PCA9555_ID_GPIO_BASE 0x30 + +#if defined(QUANTA_CPU_RGL) +#define PCA9555_CPU_LED_GPIO_BASE 0x80 + +static struct pca954x_platform_mode pca9546_cpu1_modes[] = { + MUX_INFO(0x18, 1), + MUX_INFO(0x19, 1), + MUX_INFO(0x1a, 1), + MUX_INFO(0x1b, 1), +}; + +static struct pca954x_platform_data pca9546_cpu1_data = { + .modes = pca9546_cpu1_modes, + .num_modes = 4, +}; + +static struct pca953x_platform_data pca9555_cpuled_data = { + .gpio_base = PCA9555_CPU_LED_GPIO_BASE, +}; +#endif + +static struct pca953x_platform_data pca9555psu1_data = { + .gpio_base = PCA9555_PSU_GPIO_BASE, +}; + +static struct pca953x_platform_data pca9555fan_data = { + .gpio_base = PCA9555_FAN_GPIO_BASE, +}; + +static struct pca953x_platform_data pca9555ID_data = { + .gpio_base = PCA9555_ID_GPIO_BASE, +}; + +static struct i2c_board_info ix1b_i2c_devices[] = { + { + I2C_BOARD_INFO("pca9546", 0x77), + .platform_data = &pca9546_1_data, + }, + { + I2C_BOARD_INFO("pca9546", 0x72), + .platform_data = &pca9546_2_data, + }, + { + I2C_BOARD_INFO("pca9555", 0x26), + .platform_data = &pca9555psu1_data, + }, + { + I2C_BOARD_INFO("24c02", 0x54), + }, + { + I2C_BOARD_INFO("pca9548", 0x73), + .platform_data = &pca9548sfp1_data, + }, + { + I2C_BOARD_INFO("pca9548", 0x74), + .platform_data = &pca9548sfp2_data, + }, + { + I2C_BOARD_INFO("pca9548", 0x75), + .platform_data = &pca9548sfp3_data, + }, + { + I2C_BOARD_INFO("pca9548", 0x76), + .platform_data = &pca9548sfp4_data, + }, + { + I2C_BOARD_INFO("CPLD-QSFP28", 0x38), + }, + { + I2C_BOARD_INFO("CPLD-QSFP28", 0x39), + }, + { + I2C_BOARD_INFO("pca9555", 0x23), + .platform_data = &pca9555ID_data, + }, + { + I2C_BOARD_INFO("pca9555", 0x25), + .platform_data = &pca9555fan_data, + }, + { + I2C_BOARD_INFO("qci_pmbus_ix1b", 0x5f), + }, + { + I2C_BOARD_INFO("qci_pmbus_ix1b", 0x59), + }, +#if defined(QUANTA_CPU_RGL) + { + I2C_BOARD_INFO("pca9546", 0x71), + .platform_data = &pca9546_cpu1_data, + }, + { + I2C_BOARD_INFO("pca9555", 0x20), + .platform_data = &pca9555_cpuled_data, + }, + { + I2C_BOARD_INFO("24c02", 0x50), + }, +#endif +}; + +static struct platform_driver ix1b_platform_driver = { + .driver = { + .name = "qci-ix1b", + .owner = THIS_MODULE, + }, +}; + +static struct platform_led ix1b_leds[] = { +#if defined(QUANTA_CPU_RGL) + LED_INFO(SYSLED_AMBER), + LED_INFO(SYSLED_GREEN), +#endif + LED_INFO(FRONT_PSU1_GREEN), + LED_INFO(FRONT_PSU1_RED), + LED_INFO(FRONT_PSU2_GREEN), + LED_INFO(FRONT_PSU2_RED), + LED_INFO(FRONT_FAN_GREEN), + LED_INFO(FRONT_FAN_RED), + LED_INFO(REAR_FAN1_RED), + LED_INFO(REAR_FAN2_RED), + LED_INFO(REAR_FAN3_RED), + LED_INFO(REAR_FAN4_RED), +}; + +static struct gpio_led system_led[] = { +#if defined(QUANTA_CPU_RGL) + { + .name = "sysled_amber", + .default_trigger = "timer", + .gpio = (PCA9555_CPU_LED_GPIO_BASE + 10), + .active_low = 0, + }, + { + .name = "sysled_green", + .gpio = (PCA9555_CPU_LED_GPIO_BASE + 11), + .active_low = 0, + }, +#endif + { + .name = "front_led_psu1_green", + .gpio = (PCA9555_PSU_GPIO_BASE + 10), + .active_low = 0, + }, + { + .name = "front_led_psu1_red", + .gpio = (PCA9555_PSU_GPIO_BASE + 11), + .active_low = 0, + }, + { + .name = "front_led_psu2_green", + .gpio = (PCA9555_PSU_GPIO_BASE + 12), + .active_low = 0, + }, + { + .name = "front_led_psu2_red", + .gpio = (PCA9555_PSU_GPIO_BASE + 13), + .active_low = 0, + }, + { + .name = "front_led_fan_green", + .gpio = (PCA9555_PSU_GPIO_BASE + 14), + .active_low = 0, + }, + { + .name = "front_led_fan_red", + .gpio = (PCA9555_PSU_GPIO_BASE + 15), + .active_low = 0, + }, + { + .name = "rear_led_fan1_red", + .gpio = (PCA9555_FAN_GPIO_BASE + 12), + .active_low = 0, + }, + { + .name = "rear_led_fan2_red", + .gpio = (PCA9555_FAN_GPIO_BASE + 13), + .active_low = 0, + }, + { + .name = "rear_led_fan3_red", + .gpio = (PCA9555_FAN_GPIO_BASE + 14), + .active_low = 0, + }, + { + .name = "rear_led_fan4_red", + .gpio = (PCA9555_FAN_GPIO_BASE + 15), + .active_low = 0, + }, +}; + +static struct gpio_led_platform_data system_led_data = { + .num_leds = ARRAY_SIZE(system_led), + .leds = system_led +}; + +static struct platform_device system_led_dev = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &system_led_data, + }, +}; + +static struct platform_device *ix1b_device; + +struct gpio_led_data { + struct led_classdev cdev; + unsigned gpio; + struct work_struct work; + u8 new_level; + u8 can_sleep; + u8 active_low; + u8 blinking; + int (*platform_gpio_blink_set)(unsigned gpio, int state, + unsigned long *delay_on, unsigned long *delay_off); +}; + +struct gpio_leds_priv { + int num_leds; + struct gpio_led_data leds[]; +}; + +static int __init ix1b_platform_init(void) +{ + struct i2c_client *client; + struct i2c_adapter *adapter; + struct gpio_leds_priv *priv; + int i; + int ret; + + ret = platform_driver_register(&ix1b_platform_driver); + if (ret < 0) + return ret; + + /* Register platform stuff */ + ix1b_device = platform_device_alloc("qci-ix1b", -1); + if (!ix1b_device) { + ret = -ENOMEM; + goto fail_platform_driver; + } + + ret = platform_device_add(ix1b_device); + if (ret) + goto fail_platform_device; + + adapter = i2c_get_adapter(0); + client = i2c_new_device(adapter, &ix1b_i2c_devices[0]); // pca9546_1 + printk("[CC] NEW device pca9546_1\n"); + client = i2c_new_device(adapter, &ix1b_i2c_devices[1]); // pca9546_2 + printk("[CC] NEW device pca9546_2\n"); +#if defined(QUANTA_CPU_RGL) + client = i2c_new_device(adapter, &ix1b_i2c_devices[14]); // cpu pca9546_1 + printk("[CC] NEW device cpu pca9546_1\n"); +#endif + i2c_put_adapter(adapter); + + adapter = i2c_get_adapter(0x14); + client = i2c_new_device(adapter, &ix1b_i2c_devices[12]); // pmbus_psu1 + printk("[CC] NEW device pmbus_psu1\n"); + i2c_put_adapter(adapter); + + adapter = i2c_get_adapter(0x15); + client = i2c_new_device(adapter, &ix1b_i2c_devices[13]); // pmbus_psu2 + printk("[CC] NEW device pmbus_psu2\n"); + i2c_put_adapter(adapter); + + adapter = i2c_get_adapter(0x16); + client = i2c_new_device(adapter, &ix1b_i2c_devices[2]); // pca9555-PSU1 + printk("[CC] NEW device pca9555-PSU1\n"); + client = i2c_new_device(adapter, &ix1b_i2c_devices[3]); // MB_BOARDINFO_EEPROM + printk("[CC] NEW device MB_BOARDINFO_EEPROM\n"); + client = i2c_new_device(adapter, &ix1b_i2c_devices[10]); // pca9555-ID + printk("[CC] NEW device pca9555-ID\n"); + i2c_put_adapter(adapter); + + adapter = i2c_get_adapter(0x17); + client = i2c_new_device(adapter, &ix1b_i2c_devices[11]); // pca9555-fan + printk("[CC] NEW device pca9555-fan\n"); + i2c_put_adapter(adapter); + + adapter = i2c_get_adapter(0x10); + client = i2c_new_device(adapter, &ix1b_i2c_devices[4]); // pca9548_2 SFP + printk("[CC] NEW device pca9548_2 SFP\n"); + client = i2c_new_device(adapter, &ix1b_i2c_devices[5]); // pca9548_3 SFP + printk("[CC] NEW device pca9548_3 SFP\n"); + client = i2c_new_device(adapter, &ix1b_i2c_devices[6]); // pca9548_4 SFP + printk("[CC] NEW device pca9548_4 SFP\n"); + client = i2c_new_device(adapter, &ix1b_i2c_devices[8]); // CPLD2 + printk("[CC] NEW device CPLD2\n"); + client = i2c_new_device(adapter, &ix1b_i2c_devices[9]); // CPLD3 + printk("[CC] NEW device CPLD3\n"); + i2c_put_adapter(adapter); + + adapter = i2c_get_adapter(0x11); + client = i2c_new_device(adapter, &ix1b_i2c_devices[7]); // pca9548_5 SFP + printk("[CC] NEW device pca9548_5 SFP\n"); + i2c_put_adapter(adapter); + +#if defined(QUANTA_CPU_RGL) + adapter = i2c_get_adapter(0x18); + client = i2c_new_device(adapter, &ix1b_i2c_devices[15]); // cpu pca9555_1 + printk("[CC] NEW device cpu pca9555_1\n"); + i2c_put_adapter(adapter); + + for(i = 0x20; i < 0x40; i++) + { + adapter = i2c_get_adapter(i); + client = i2c_new_device(adapter, &ix1b_i2c_devices[16]); // eeprom for loopback module + i2c_put_adapter(adapter); + } + printk("[CC] NEW device eeprom\n"); +#endif + + /* Register LED devices */ + platform_device_register(&system_led_dev); + priv = platform_get_drvdata(&system_led_dev); + for (i = 0; i < priv->num_leds; i++) { + printk("leds-gpio gpio-%d: Register led %s\n", priv->leds[i].gpio, priv->leds[i].cdev.name); + ix1b_leds[i].cdev = &(priv->leds[i].cdev); + } + printk("[CC] NEW device led\n"); + + return 0; + +fail_platform_device: + platform_device_put(ix1b_device); + +fail_platform_driver: + platform_driver_unregister(&ix1b_platform_driver); + return ret; +} + +static void __exit ix1b_platform_exit(void) +{ + platform_device_unregister(ix1b_device); + platform_driver_unregister(&ix1b_platform_driver); +} + +module_init(ix1b_platform_init); +module_exit(ix1b_platform_exit); + + +MODULE_AUTHOR("Quanta Computer Inc."); +MODULE_DESCRIPTION("Quanta IX1B Platform Driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/qci_pmbus.c b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/qci_pmbus.c new file mode 100755 index 000000000000..f54a0f204d67 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/modules/qci_pmbus.c @@ -0,0 +1,487 @@ +/* + * QUANTA Generic PMBUS driver + * + * + * Based on generic pmbus driver and ltc2978 driver + * + * Author: Chih-Pei Chang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +//#include <../drivers/hwmon/pmbus/pmbus.h> +#include "pmbus.h" +#include + +enum projects { ly8, ix1, ix2, ix1b }; + +#define DELAY_TIME 1000 /* uS */ + +/* Needed to access the mutex. Copied from pmbus_core.c */ +#define PB_STATUS_BASE 0 +#define PB_STATUS_VOUT_BASE (PB_STATUS_BASE + PMBUS_PAGES) +#define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES) +#define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES) +#define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES) +#define PB_STATUS_TEMP_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES) +#define PB_STATUS_INPUT_BASE (PB_STATUS_TEMP_BASE + PMBUS_PAGES) +#define PB_STATUS_VMON_BASE (PB_STATUS_INPUT_BASE + 1) +#define PB_NUM_STATUS_REG (PB_STATUS_VMON_BASE + 1) +struct pmbus_data { + struct device *dev; + struct device *hwmon_dev; + + u32 flags; /* from platform data */ + + int exponent[PMBUS_PAGES]; + /* linear mode: exponent for output voltages */ + + const struct pmbus_driver_info *info; + + int max_attributes; + int num_attributes; + struct attribute_group group; + const struct attribute_group *groups[2]; + + struct pmbus_sensor *sensors; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* + * A single status register covers multiple attributes, + * so we keep them all together. + */ + u8 status[PB_NUM_STATUS_REG]; + u8 status_register; + + u8 currpage; +}; + +static int qci_pmbus_read_block(struct i2c_client *client, u8 command, int data_len, u8 *data) +{ + int result = 0; + int retry_count = 3; + + while (retry_count) { + retry_count--; + + result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); + + if (result < 0) { + msleep(10); + continue; + } + + result = 0; + break; + } + + return result; +} + +static ssize_t qci_pmbus_show_mfr_id(struct device *dev, + struct device_attribute *da, char *buf) +{ + int ret, len; + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1], *str; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + ret = qci_pmbus_read_block(client, PMBUS_MFR_ID, I2C_SMBUS_BLOCK_MAX, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer ID\n"); + return ret; + } + len = block_buffer[0]; + block_buffer[(len+1)] = '\0'; + str = &(block_buffer[1]); + + return snprintf(buf, PAGE_SIZE, "%s\n", str); +} + +static ssize_t qci_pmbus_show_mfr_model(struct device *dev, + struct device_attribute *da, char *buf) +{ + int ret, len; + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1], *str; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + ret = qci_pmbus_read_block(client, PMBUS_MFR_MODEL, I2C_SMBUS_BLOCK_MAX, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Model\n"); + return ret; + } + len = block_buffer[0]; + block_buffer[(len+1)] = '\0'; + str = &(block_buffer[1]); + + return snprintf(buf, PAGE_SIZE, "%s\n", str); +} + +static ssize_t qci_pmbus_show_mfr_revision(struct device *dev, + struct device_attribute *da, char *buf) +{ + int ret, len; + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1], *str; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + ret = qci_pmbus_read_block(client, PMBUS_MFR_REVISION, I2C_SMBUS_BLOCK_MAX, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Revision\n"); + return ret; + } + len = block_buffer[0]; + block_buffer[(len+1)] = '\0'; + str = &(block_buffer[1]); + + return snprintf(buf, PAGE_SIZE, "%s\n", str); +} + +static ssize_t qci_pmbus_show_mfr_location(struct device *dev, + struct device_attribute *da, char *buf) +{ + int ret, len; + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1], *str; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + ret = qci_pmbus_read_block(client, PMBUS_MFR_LOCATION, I2C_SMBUS_BLOCK_MAX, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacture Location\n"); + return ret; + } + len = block_buffer[0]; + block_buffer[(len+1)] = '\0'; + str = &(block_buffer[1]); + + return snprintf(buf, PAGE_SIZE, "%s\n", str); +} + +static ssize_t qci_pmbus_show_mfr_serial(struct device *dev, + struct device_attribute *da, char *buf) +{ + int ret, len; + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1], *str; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + ret = qci_pmbus_read_block(client, PMBUS_MFR_SERIAL, I2C_SMBUS_BLOCK_MAX, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Serial\n"); + return ret; + } + len = block_buffer[0]; + block_buffer[(len+1)] = '\0'; + str = &(block_buffer[1]); + + return snprintf(buf, PAGE_SIZE, "%s\n", str); +} + + +static DEVICE_ATTR(mfr_id, S_IRUGO, qci_pmbus_show_mfr_id, NULL); +static DEVICE_ATTR(mfr_model, S_IRUGO, qci_pmbus_show_mfr_model, NULL); +static DEVICE_ATTR(mfr_revision, S_IRUGO, qci_pmbus_show_mfr_revision, NULL); +static DEVICE_ATTR(mfr_location, S_IRUGO, qci_pmbus_show_mfr_location, NULL); +static DEVICE_ATTR(mfr_serial, S_IRUGO, qci_pmbus_show_mfr_serial, NULL); + + +static struct attribute *qci_pmbus_inventory_attrs[] = { + &dev_attr_mfr_id.attr, + &dev_attr_mfr_model.attr, + &dev_attr_mfr_revision.attr, + &dev_attr_mfr_location.attr, + &dev_attr_mfr_serial.attr, + NULL +}; + +static struct attribute_group qci_pmbus_inventory_attr_grp = { + .attrs = qci_pmbus_inventory_attrs +}; + +/* FIXME: add project specific id here */ +static const struct i2c_device_id qci_pmbus_id[] = { + {"qci_pmbus_ly8", ly8}, + {"qci_pmbus_ix1", ix1}, + {"qci_pmbus_ix2", ix2}, + {"qci_pmbus_ix1b", ix1b}, + {} +}; +MODULE_DEVICE_TABLE(i2c, qci_pmbus_id); + +/* + * Find sensor groups and status registers on each page. + */ +static void qci_pmbus_find_sensor_groups(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int page; + + /* Sensors detected on page 0 only */ + if (pmbus_check_word_register(client, 0, PMBUS_READ_VIN)) + info->func[0] |= PMBUS_HAVE_VIN; + if (pmbus_check_word_register(client, 0, PMBUS_READ_VCAP)) + info->func[0] |= PMBUS_HAVE_VCAP; + if (pmbus_check_word_register(client, 0, PMBUS_READ_IIN)) + info->func[0] |= PMBUS_HAVE_IIN; + if (pmbus_check_word_register(client, 0, PMBUS_READ_PIN)) + info->func[0] |= PMBUS_HAVE_PIN; + if (info->func[0] + && pmbus_check_byte_register(client, 0, PMBUS_STATUS_INPUT)) + info->func[0] |= PMBUS_HAVE_STATUS_INPUT; + if (pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_12) && + pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_1)) { + info->func[0] |= PMBUS_HAVE_FAN12; + if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_12)) + info->func[0] |= PMBUS_HAVE_STATUS_FAN12; + } + if (pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_34) && + pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_3)) { + info->func[0] |= PMBUS_HAVE_FAN34; + if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_34)) + info->func[0] |= PMBUS_HAVE_STATUS_FAN34; + } + if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_1)) + info->func[0] |= PMBUS_HAVE_TEMP; + if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_2)) + info->func[0] |= PMBUS_HAVE_TEMP2; + if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_3)) + info->func[0] |= PMBUS_HAVE_TEMP3; + if (info->func[0] & (PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 + | PMBUS_HAVE_TEMP3) + && pmbus_check_byte_register(client, 0, + PMBUS_STATUS_TEMPERATURE)) + info->func[0] |= PMBUS_HAVE_STATUS_TEMP; + + /* Sensors detected on all pages */ + for (page = 0; page < info->pages; page++) { + if (pmbus_check_word_register(client, page, PMBUS_READ_VOUT)) { + info->func[page] |= PMBUS_HAVE_VOUT; + if (pmbus_check_byte_register(client, page, + PMBUS_STATUS_VOUT)) + info->func[page] |= PMBUS_HAVE_STATUS_VOUT; + } + if (pmbus_check_word_register(client, page, PMBUS_READ_IOUT)) { + info->func[page] |= PMBUS_HAVE_IOUT; + if (pmbus_check_byte_register(client, 0, + PMBUS_STATUS_IOUT)) + info->func[page] |= PMBUS_HAVE_STATUS_IOUT; + } + if (pmbus_check_word_register(client, page, PMBUS_READ_POUT)) + info->func[page] |= PMBUS_HAVE_POUT; + } +} + +/* + * Identify chip parameters. + */ +static int qci_pmbus_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int ret = 0; + + if (!info->pages) { + /* + * Check if the PAGE command is supported. If it is, + * keep setting the page number until it fails or until the + * maximum number of pages has been reached. Assume that + * this is the number of pages supported by the chip. + */ + if (pmbus_check_byte_register(client, 0, PMBUS_PAGE)) { + int page; + + for (page = 1; page < PMBUS_PAGES; page++) { + if (pmbus_set_page(client, page) < 0) + break; + } + pmbus_set_page(client, 0); + info->pages = page; + } else { + info->pages = 1; + } + } + + if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) { + int vout_mode; + + vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); + if (vout_mode >= 0 && vout_mode != 0xff) { + switch (vout_mode >> 5) { + case 0: + break; + case 1: + info->format[PSC_VOLTAGE_OUT] = vid; + break; + case 2: + info->format[PSC_VOLTAGE_OUT] = direct; + break; + default: + ret = -ENODEV; + goto abort; + } + } + } + + /* + * We should check if the COEFFICIENTS register is supported. + * If it is, and the chip is configured for direct mode, we can read + * the coefficients from the chip, one set per group of sensor + * registers. + * + * To do this, we will need access to a chip which actually supports the + * COEFFICIENTS command, since the command is too complex to implement + * without testing it. Until then, abort if a chip configured for direct + * mode was detected. + */ + if (info->format[PSC_VOLTAGE_OUT] == direct) { + ret = -ENODEV; + goto abort; + } + + /* if no function pre-defined, try to find sensor groups */ + if (info->func[0] == 0) qci_pmbus_find_sensor_groups(client, info); +abort: + return ret; +} + +int qci_pmbus_set_page(struct i2c_client *client, u8 page) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int rv = 0; + int newpage; + + if (page != data->currpage) { + rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + udelay(DELAY_TIME); + newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE); + if (newpage != page) + rv = -EIO; + else + data->currpage = page; + } + return rv; +} + +int qci_write_byte(struct i2c_client *client, int page, u8 value) +{ + int rv; + + if (page >= 0) { + rv = qci_pmbus_set_page(client, page); + if (rv < 0) + return rv; + } + + rv = i2c_smbus_write_byte(client, value); + udelay(DELAY_TIME); + return rv; +} + +int qci_write_word_data(struct i2c_client *client, int page, int reg, u16 word) +{ + int rv; + + rv = qci_pmbus_set_page(client, page); + if (rv < 0) + return rv; + + rv = i2c_smbus_write_word_data(client, reg, word); + udelay(DELAY_TIME); + return rv; +} + +static int qci_pmbus_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct pmbus_driver_info *info; + int ret, i; + + dev_info(dev, "qci_pmbus_probe\n"); + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENODEV; + + info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info), + GFP_KERNEL); + + if (!info) + return -ENOMEM; + + info->func[0] = 0; + + /* FIXME: add project specific function table here */ + switch (id->driver_data) { + case ly8: + info->pages = 1; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + ; + break; + case ix1: + case ix2: + case ix1b: + info->pages = 1; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 + | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 + | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT + ; + break; + default: + break; + } + info->write_word_data = qci_write_word_data; + info->write_byte = qci_write_byte; + info->identify = qci_pmbus_identify; /* FIXME: reserve for future use */ + + /* Register sysfs hooks */ + ret = sysfs_create_group(&dev->kobj, &qci_pmbus_inventory_attr_grp); + if (ret) { + dev_err(dev, "Failed to create sysfs entries\n"); + return -1; + } + + return pmbus_do_probe(client, id, info); +} + +/* This is the driver that will be inserted */ +static struct i2c_driver qci_pmbus_driver = { + .driver = { + .name = "qci-pmbus", + }, + .probe = qci_pmbus_probe, + .remove = pmbus_do_remove, + .id_table = qci_pmbus_id, +}; + +module_i2c_driver(qci_pmbus_driver); + + +MODULE_AUTHOR("Quanta Computer Inc."); +MODULE_VERSION("1.0"); +MODULE_DESCRIPTION("QUANTA generic PMBus driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/service/ix1b-platform-init.service b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/service/ix1b-platform-init.service new file mode 100755 index 000000000000..a51c0cca6914 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/service/ix1b-platform-init.service @@ -0,0 +1,13 @@ +[Unit] +Description=Quanta IX1B-32X Platform initialization service +Before=pmon.service +DefaultDependencies=no + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/quanta_ix1b_util.py install +ExecStop=/usr/local/bin/quanta_ix1b_util.py clean +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/setup.py b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/setup.py new file mode 100755 index 000000000000..e37687db7eca --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/setup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +import os +import sys +from setuptools import setup +os.listdir + +setup( + name='ix1b_32x', + version='1.0', + description='Module to initialize Quanta IX1B-32X platforms', + + packages=['ix1b_32x'], + package_dir={'ix1b_32x': 'ix1b-32x/classes'}, +) + diff --git a/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/utils/quanta_ix1b_util.py b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/utils/quanta_ix1b_util.py new file mode 100755 index 000000000000..f530cf3dbb7b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-quanta/ix1b-32x/utils/quanta_ix1b_util.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python +# +# Copyright (C) 2018 Quanta Computer Inc. +# +# 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 3 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, see . + +""" +Usage: %(scriptName)s [options] command object + +options: + -h | --help : this help message + -d | --debug : run with debug mode + -f | --force : ignore error during installation or clean +command: + install : install drivers and generate related sysfs nodes + clean : uninstall drivers and remove related sysfs nodes +""" + +import os +import commands +import sys, getopt +import logging +import re +import time +from collections import namedtuple + +DEBUG = False +args = [] +FORCE = 0 +i2c_prefix = '/sys/bus/i2c/devices/' + + +if DEBUG == True: + print sys.argv[0] + print 'ARGV :', sys.argv[1:] + + +def main(): + global DEBUG + global args + global FORCE + + if len(sys.argv)<2: + show_help() + + options, args = getopt.getopt(sys.argv[1:], 'hdf', ['help', + 'debug', + 'force', + ]) + if DEBUG == True: + print options + print args + print len(sys.argv) + + for opt, arg in options: + if opt in ('-h', '--help'): + show_help() + elif opt in ('-d', '--debug'): + DEBUG = True + logging.basicConfig(level=logging.INFO) + elif opt in ('-f', '--force'): + FORCE = 1 + else: + logging.info('no option') + for arg in args: + if arg == 'install': + install() + elif arg == 'clean': + uninstall() + else: + show_help() + + + return 0 + +def show_help(): + print __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]} + sys.exit(0) + +def show_log(txt): + if DEBUG == True: + print "[IX1B-32X]"+txt + return + +def exec_cmd(cmd, show): + logging.info('Run :'+cmd) + status, output = commands.getstatusoutput(cmd) + show_log (cmd +"with result:" + str(status)) + show_log (" output:"+output) + if status: + logging.info('Failed :'+cmd) + if show: + print('Failed :'+cmd) + return status, output + +instantiate =[ +#turn on module power +'echo 53 > /sys/class/gpio/export', +'echo out > /sys/class/gpio/gpio53/direction', +'echo 1 >/sys/class/gpio/gpio53/value', +#turn on 100G led by default +'i2cset -y 0x13 0x38 0x00 0xff', +'i2cset -y 0x13 0x38 0x01 0xff', +'i2cset -y 0x13 0x39 0x00 0xff', +'i2cset -y 0x13 0x39 0x01 0xff' +] + +drivers =[ +'lpc_ich', +'i2c-i801', +'i2c-dev', +'i2c-mux-pca954x', +'gpio-pca953x', +'qci_pmbus', +'leds-gpio', +'qci_cpld_qsfp28', +'qci_platform_ix1b' +] + + + +def system_install(): + global FORCE + + #remove default drivers to avoid modprobe order conflicts + status, output = exec_cmd("rmmod i2c_ismt ", 1) + status, output = exec_cmd("rmmod i2c-i801 ", 1) + #setup driver dependency + status, output = exec_cmd("depmod -a ", 1) + #install drivers + for i in range(0,len(drivers)): + status, output = exec_cmd("modprobe "+drivers[i], 1) + if status: + print output + if FORCE == 0: + return status + + #instantiate devices + for i in range(0,len(instantiate)): + time.sleep(1) + status, output = exec_cmd(instantiate[i], 1) + if status: + print output + if FORCE == 0: + return status + + #for i in range(22,30): + # status, output =exec_cmd("echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-4/i2c-"+str(i)+"/new_device", 1) + # if status: + # print output + # if FORCE == 0: + # return status + + return + + +def system_ready(): + if not device_found(): + return False + return True + +def install(): + if not device_found(): + print "No device, installing...." + status = system_install() + if status: + if FORCE == 0: + return status + else: + print " ix1b driver already installed...." + return + +def uninstall(): + global FORCE + #uninstall drivers + for i in range(len(drivers)-1,-1,-1): + status, output = exec_cmd("rmmod "+drivers[i], 1) + if status: + print output + if FORCE == 0: + return status + return + +def device_found(): + ret1, log = exec_cmd("ls "+i2c_prefix+"i2c-0", 0) + return ret1 + +if __name__ == "__main__": + main() + + + diff --git a/platform/broadcom/sonic-platform-modules-s6000 b/platform/broadcom/sonic-platform-modules-s6000 deleted file mode 160000 index ed9ea067b5cb..000000000000 --- a/platform/broadcom/sonic-platform-modules-s6000 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ed9ea067b5cbfd7d1c835a903729f9dc28d3b035 diff --git a/platform/broadcom/sonic-platform-modules-s6000/LICENSE b/platform/broadcom/sonic-platform-modules-s6000/LICENSE new file mode 100644 index 000000000000..676cdeec726b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/LICENSE @@ -0,0 +1,15 @@ +Copyright (C) 2016 Microsoft, Inc + +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. diff --git a/platform/broadcom/sonic-platform-modules-s6000/MAINTAINERS b/platform/broadcom/sonic-platform-modules-s6000/MAINTAINERS new file mode 100644 index 000000000000..65e3129fcc13 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/MAINTAINERS @@ -0,0 +1,7 @@ +# This file describes the maintainers for sonic-platform-modules-s6000 +# See the SONiC project governance document for more information + +Name = "Shuotian Cheng" +Email = "shuche@microsoft.com" +Github = stcheng +Mailinglist = sonicproject@googlegroups.com diff --git a/platform/broadcom/sonic-platform-modules-s6000/README.md b/platform/broadcom/sonic-platform-modules-s6000/README.md new file mode 100644 index 000000000000..7913d168a30a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/README.md @@ -0,0 +1,2 @@ +# sonic-platform-modules-s6000 +Device drivers for support of Dell S6000 for the SONiC project diff --git a/platform/broadcom/sonic-platform-modules-s6000/debian/changelog b/platform/broadcom/sonic-platform-modules-s6000/debian/changelog new file mode 100644 index 000000000000..0a6b7820bdeb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/debian/changelog @@ -0,0 +1,5 @@ +platform-modules-s6000 (1.0) unstable; urgency=low + + * Initial release + + -- Shuotian Cheng Mon, 11 Nov 2015 11:11:11 -0800 diff --git a/platform/broadcom/sonic-platform-modules-s6000/debian/compat b/platform/broadcom/sonic-platform-modules-s6000/debian/compat new file mode 100644 index 000000000000..45a4fb75db86 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/debian/compat @@ -0,0 +1 @@ +8 diff --git a/platform/broadcom/sonic-platform-modules-s6000/debian/control b/platform/broadcom/sonic-platform-modules-s6000/debian/control new file mode 100644 index 000000000000..394a1f4aa4af --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/debian/control @@ -0,0 +1,12 @@ +Source: platform-modules-s6000 +Section: main +Priority: extra +Maintainer: Shuotian Cheng +Build-Depends: debhelper (>= 8.0.0), bzip2 +Standards-Version: 3.9.3 + +Package: platform-modules-s6000 +Architecture: amd64 +Depends: linux-image-3.16.0-5-amd64 +Description: kernel modules for platform devices such as fan, led, sfp + diff --git a/platform/broadcom/sonic-platform-modules-s6000/debian/copyright b/platform/broadcom/sonic-platform-modules-s6000/debian/copyright new file mode 100644 index 000000000000..6fbc5a7f6ca0 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/debian/copyright @@ -0,0 +1,16 @@ +Provides linux sysfs interface to Dell S6000 platform hardware peripherals +Copyright (C) 2016 Microsoft + +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. diff --git a/platform/broadcom/sonic-platform-modules-s6000/debian/platform-modules-s6000.init b/platform/broadcom/sonic-platform-modules-s6000/debian/platform-modules-s6000.init new file mode 100755 index 000000000000..1c7047cc7611 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/debian/platform-modules-s6000.init @@ -0,0 +1,45 @@ +#!/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 S6000 board. +### END INIT INFO + +case "$1" in +start) + echo -n "Setting up board... " + + depmod -a + modprobe i2c_mux_gpio + modprobe dell_s6000_platform + + /usr/local/bin/set-fan-speed 15000 + /usr/local/bin/reset-qsfp + + echo "done." + ;; + +stop) + echo "done." + rmmod dell_s6000_platform + rmmod i2c_mux_gpio + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/platform-modules-s6000.init {start|stop}" + exit 1 + ;; +esac + +exit 0 + diff --git a/platform/broadcom/sonic-platform-modules-s6000/debian/rules b/platform/broadcom/sonic-platform-modules-s6000/debian/rules new file mode 100755 index 000000000000..9912e052b551 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/debian/rules @@ -0,0 +1,32 @@ +#!/usr/bin/make -f + +export INSTALL_MOD_DIR:=extra + +PACKAGE_NAME := platform-modules-s6000 +KVERSION ?= $(shell uname -r) +KERNEL_SRC := /lib/modules/$(KVERSION) +MODULE_SRC := $(shell pwd)/modules +SCRIPT_SRC := $(shell pwd)/scripts + +%: + dh $@ + +override_dh_auto_build: + make -C $(KERNEL_SRC)/build M=$(MODULE_SRC) + +override_dh_auto_install: + dh_installdirs -p$(PACKAGE_NAME) $(KERNEL_SRC)/$(INSTALL_MOD_DIR) + cp $(MODULE_SRC)/*.ko debian/$(PACKAGE_NAME)/$(KERNEL_SRC)/$(INSTALL_MOD_DIR) + dh_installdirs -p$(PACKAGE_NAME) usr/local/bin + cp -r $(SCRIPT_SRC)/* debian/$(PACKAGE_NAME)/usr/local/bin + +override_dh_usrlocal: + +override_dh_pysupport: + +override_dh_clean: + dh_clean + rm -f $(MODULE_SRC)/*.o $(MODULE_SRC)/*.ko $(MODULE_SRC)/*.mod.c $(MODULE_SRC)/.*.cmd + rm -f $(MODULE_SRC)/Module.markers $(MODULE_SRC)/Module.symvers $(MODULE_SRC)/modules.order + rm -rf $(MODULE_SRC)/.tmp_versions + diff --git a/platform/broadcom/sonic-platform-modules-s6000/modules/Makefile b/platform/broadcom/sonic-platform-modules-s6000/modules/Makefile new file mode 100644 index 000000000000..12e80d01010c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/modules/Makefile @@ -0,0 +1 @@ +obj-m := dell_s6000_platform.o diff --git a/platform/broadcom/sonic-platform-modules-s6000/modules/dell_s6000_platform.c b/platform/broadcom/sonic-platform-modules-s6000/modules/dell_s6000_platform.c new file mode 100644 index 000000000000..d2a92770cecb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/modules/dell_s6000_platform.c @@ -0,0 +1,2031 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define S6000_MUX_BASE_NR 10 +#define QSFP_MODULE_BASE_NR 20 + +/* 74CBTLV3253 Dual 1-of-4 multiplexer/demultiplexer */ +#define MUX_CHANNEL_NUM 2 + +#define CPLD_DEVICE_NUM 3 +#define QSFP_MODULE_NUM 16 +#define QSFP_DEVICE_NUM 2 + +#define SFF8436_INFO(data) \ + .type = "sff8436", .addr = 0x50, .platform_data = (data) + +#define SFF_8346_PORT(eedata) \ + .byte_len = 128, .page_size = 1, .flags = SFF_8436_FLAG_READONLY + +static void device_release(struct device *dev) +{ + return; +} + +/* + * S6000 74CBTLV3253 MUX + */ +static const unsigned s6000_mux_gpios[] = { + 1, 2 +}; + +static const unsigned s6000_mux_values[] = { + 0, 1, 2, 3 +}; + +static struct i2c_mux_gpio_platform_data s6000_mux_platform_data = { + .parent = 2, + .base_nr = S6000_MUX_BASE_NR, + .values = s6000_mux_values, + .n_values = ARRAY_SIZE(s6000_mux_values), + .gpios = s6000_mux_gpios, + .n_gpios = ARRAY_SIZE(s6000_mux_gpios), + .idle = 0, +}; + +static struct platform_device s6000_mux = { + .name = "i2c-mux-gpio", + .id = 0, + .dev = { + .platform_data = &s6000_mux_platform_data, + .release = device_release + }, +}; + +/* + * S6000 CPLD + */ + +enum cpld_type { + system_cpld, + master_cpld, + slave_cpld, +}; + +struct cpld_platform_data { + int reg_addr; + struct i2c_client *client; +}; + +static struct cpld_platform_data s6000_cpld_platform_data[] = { + [system_cpld] = { + .reg_addr = 0x31, + }, + + [master_cpld] = { + .reg_addr = 0x32, + }, + + [slave_cpld] = { + .reg_addr = 0x33, + }, +}; + +static struct platform_device s6000_cpld = { + .name = "dell-s6000-cpld", + .id = 0, + .dev = { + .platform_data = s6000_cpld_platform_data, + .release = device_release + }, +}; + +/* + * S6000 QSFP MUX + */ + +struct qsfp_mux_platform_data { + int parent; + int base_nr; + int reg_addr; + struct i2c_client *cpld; +}; + +struct qsfp_mux { + struct i2c_adapter *parent; + struct i2c_adapter **child; + struct qsfp_mux_platform_data data; +}; +static struct qsfp_mux_platform_data s6000_qsfp_mux_platform_data[] = { + { + .parent = S6000_MUX_BASE_NR + 2, + .base_nr = QSFP_MODULE_BASE_NR, + .cpld = NULL, + .reg_addr = 0x0, + }, + { + .parent = S6000_MUX_BASE_NR + 3, + .base_nr = QSFP_MODULE_BASE_NR + QSFP_MODULE_NUM, + .cpld = NULL, + .reg_addr = 0xa, + }, +}; + +static struct platform_device s6000_qsfp_mux[] = { + { + .name = "dell-s6000-qsfp-mux", + .id = 0, + .dev = { + .platform_data = &s6000_qsfp_mux_platform_data[0], + .release = device_release, + }, + }, + { + .name = "dell-s6000-qsfp-mux", + .id = 1, + .dev = { + .platform_data = &s6000_qsfp_mux_platform_data[1], + .release = device_release, + }, + }, +}; + +/* + * S6000 I2C DEVICES + */ + +struct i2c_device_platform_data { + int parent; + struct i2c_board_info info; + struct i2c_client *client; +}; + +static struct sff_8436_platform_data sff_8436_port[] = { + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, + { SFF_8346_PORT() }, +}; + +static struct i2c_device_platform_data s6000_i2c_device_platform_data[] = { + { + /* PSU 1 FRU EEPROM */ + .parent = 1, + .info = { I2C_BOARD_INFO("24c02", 0x50) }, + .client = NULL, + }, + { + /* PSU 2 FRU EEPROM */ + .parent = 1, + .info = { I2C_BOARD_INFO("24c02", 0x51) }, + }, + { + /* PSU 1 PMBUS */ + .parent = 1, + .info = { I2C_BOARD_INFO("dni_dps460", 0x58) }, + }, + { + /* PSU 2 PMBUS */ + .parent = 1, + .info = { I2C_BOARD_INFO("dni_dps460", 0x59) }, + }, + { + /* TEMP Sensor EMC1428-7 */ + .parent = S6000_MUX_BASE_NR, + .info = { I2C_BOARD_INFO("emc1403", 0x4d) }, + }, + { + /* JEDEC JC 42.4 compliant temperature sensors */ + .parent = S6000_MUX_BASE_NR, + .info = { I2C_BOARD_INFO("jc42", 0x18) }, + }, + { + /* DDR3 MODULE SPD */ + .parent = S6000_MUX_BASE_NR, + .info = { I2C_BOARD_INFO("spd", 0x50) } , + }, + { + /* + * ID EEPROM + * AT24C64D-SSHM-T + */ + .parent = S6000_MUX_BASE_NR, + .info = { I2C_BOARD_INFO("24c02", 0x53) } , + }, + { + /* + * FAN Tray Controller 1 + * MAX6620ATI+T + */ + .parent = S6000_MUX_BASE_NR + 1, + .info = { I2C_BOARD_INFO("max6620", 0x29) }, + }, + { + /* + * FAN Tray Controller 2 + * MAX6620ATI+T + */ + .parent = S6000_MUX_BASE_NR + 1, + .info = { I2C_BOARD_INFO("max6620", 0x2a) }, + }, + { + /* + * Hot-Swap PSU 1 + * LTC1451UFD#TRPBF + */ + .parent = S6000_MUX_BASE_NR + 1, + .info = { I2C_BOARD_INFO("ltc4215", 0x40) }, + }, + { + /* + * Hot-Swap PSU 2 + * LTC1451UFD#TRPBF + */ + .parent = S6000_MUX_BASE_NR + 1, + .info = { I2C_BOARD_INFO("ltc4215", 0x42) }, + }, + { + /* + * Temp Sensor MAC + * TMP75AIDR + */ + .parent = S6000_MUX_BASE_NR + 1, + .info = { I2C_BOARD_INFO("tmp75", 0x4c) }, + }, + { + /* + * Temp Sensor NIC + * TMP75AIDR + */ + .parent = S6000_MUX_BASE_NR + 1, + .info = { I2C_BOARD_INFO("tmp75", 0x4d) }, + }, + { + /* + * Temp Sensor AMB + * TMP75AIDR + */ + .parent = S6000_MUX_BASE_NR + 1, + .info = { I2C_BOARD_INFO("tmp75", 0x4e) }, + }, + { + /* + * FAN Tray 1 EEPROM + * M24C02-WMN6TP + */ + .parent = S6000_MUX_BASE_NR + 1, + .info = { I2C_BOARD_INFO("24c02", 0x51) }, + }, + { + /* + * FAN Tray 2 EEPROM + * M24C02-WMN6TP + */ + .parent = S6000_MUX_BASE_NR + 1, + .info = { I2C_BOARD_INFO("24c02", 0x52) }, + }, + { + /* + * FAN Tray 3 EEPROM + * M24C02-WMN6TP + */ + .parent = S6000_MUX_BASE_NR + 1, + .info = { I2C_BOARD_INFO("24c02", 0x53) }, + }, + /* QSFP Modules */ + { + .parent = QSFP_MODULE_BASE_NR, + .info = { SFF8436_INFO(&sff_8436_port[0]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 1, + .info = { SFF8436_INFO(&sff_8436_port[1]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 2, + .info = { SFF8436_INFO(&sff_8436_port[2]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 3, + .info = { SFF8436_INFO(&sff_8436_port[3]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 4, + .info = { SFF8436_INFO(&sff_8436_port[4]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 5, + .info = { SFF8436_INFO(&sff_8436_port[5]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 6, + .info = { SFF8436_INFO(&sff_8436_port[6]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 7, + .info = { SFF8436_INFO(&sff_8436_port[7]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 8, + .info = { SFF8436_INFO(&sff_8436_port[8]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 9, + .info = { SFF8436_INFO(&sff_8436_port[9]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 10, + .info = { SFF8436_INFO(&sff_8436_port[10]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 11, + .info = { SFF8436_INFO(&sff_8436_port[11]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 12, + .info = { SFF8436_INFO(&sff_8436_port[12]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 13, + .info = { SFF8436_INFO(&sff_8436_port[13]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 14, + .info = { SFF8436_INFO(&sff_8436_port[14]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 15, + .info = { SFF8436_INFO(&sff_8436_port[15]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 16, + .info = { SFF8436_INFO(&sff_8436_port[16]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 17, + .info = { SFF8436_INFO(&sff_8436_port[17]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 18, + .info = { SFF8436_INFO(&sff_8436_port[18]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 19, + .info = { SFF8436_INFO(&sff_8436_port[19]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 20, + .info = { SFF8436_INFO(&sff_8436_port[20]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 21, + .info = { SFF8436_INFO(&sff_8436_port[21]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 22, + .info = { SFF8436_INFO(&sff_8436_port[22]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 23, + .info = { SFF8436_INFO(&sff_8436_port[23]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 24, + .info = { SFF8436_INFO(&sff_8436_port[24]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 25, + .info = { SFF8436_INFO(&sff_8436_port[25]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 26, + .info = { SFF8436_INFO(&sff_8436_port[26]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 27, + .info = { SFF8436_INFO(&sff_8436_port[27]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 28, + .info = { SFF8436_INFO(&sff_8436_port[28]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 29, + .info = { SFF8436_INFO(&sff_8436_port[29]) }, + }, + { + .parent = QSFP_MODULE_BASE_NR + 30, + .info = { SFF8436_INFO(&sff_8436_port[30]) }, + .client = NULL, + }, + { + .parent = QSFP_MODULE_BASE_NR + 31, + .info = { SFF8436_INFO(&sff_8436_port[31]) }, + .client = NULL, + }, +}; + +static struct platform_device s6000_i2c_device[] = { + { + .name = "dell-s6000-i2c-device", + .id = 0, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[0], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 1, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[1], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 2, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[2], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 3, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[3], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 4, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[4], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 5, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[5], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 6, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[6], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 7, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[7], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 8, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[8], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 9, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[9], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 10, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[10], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 11, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[11], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 12, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[12], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 13, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[13], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 14, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[14], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 15, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[15], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 16, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[16], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 17, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[17], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 18, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[18], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 19, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[19], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 20, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[20], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 21, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[21], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 22, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[22], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 23, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[23], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 24, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[24], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 25, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[25], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 26, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[26], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 27, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[27], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 28, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[28], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 29, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[29], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 30, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[30], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 31, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[31], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 32, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[32], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 33, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[33], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 34, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[34], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 35, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[35], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 36, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[36], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 37, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[37], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 38, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[38], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 39, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[39], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 40, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[40], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 41, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[41], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 42, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[42], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 43, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[43], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 44, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[44], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 45, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[45], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 46, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[46], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 47, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[47], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 48, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[48], + .release = device_release, + }, + }, + { + .name = "dell-s6000-i2c-device", + .id = 49, + .dev = { + .platform_data = &s6000_i2c_device_platform_data[49], + .release = device_release, + }, + }, +}; + + + +static int cpld_reg_write_byte(struct i2c_client *client, u8 regaddr, u8 val) +{ + union i2c_smbus_data data; + + data.byte = val; + return client->adapter->algo->smbus_xfer(client->adapter, client->addr, + client->flags, + I2C_SMBUS_WRITE, + regaddr, I2C_SMBUS_BYTE_DATA, &data); +} + +static int qsfp_mux_select(struct i2c_adapter *adap, void *data, u32 chan) +{ + struct qsfp_mux *mux = data; + unsigned short mask = ~(unsigned short)(1 << chan); + + cpld_reg_write_byte(mux->data.cpld, mux->data.reg_addr, (u8)(mask & 0xff)); + return cpld_reg_write_byte(mux->data.cpld, mux->data.reg_addr + 1, (u8)(mask >> 8)); +} + +static int __init qsfp_mux_probe(struct platform_device *pdev) +{ + struct qsfp_mux *mux; + struct qsfp_mux_platform_data *pdata; + struct i2c_adapter *parent; + int i, ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -ENODEV; + } + + parent = i2c_get_adapter(pdata->parent); + if (!parent) { + dev_err(&pdev->dev, "Parent adapter (%d) not found\n", + pdata->parent); + return -ENODEV; + } + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) { + ret = -ENOMEM; + goto alloc_failed; + } + + mux->parent = parent; + mux->data = *pdata; + mux->child = kzalloc(sizeof(struct i2c_adapter *) * QSFP_MODULE_NUM, GFP_KERNEL); + if (!mux->child) { + ret = -ENOMEM; + goto alloc_failed2; + } + + for (i = 0; i < QSFP_MODULE_NUM; i++) { + int nr = pdata->base_nr + i; + unsigned int class = 0; + + mux->child[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, + nr, i, class, + qsfp_mux_select, NULL); + if (!mux->child[i]) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed to add adapter %d\n", i); + goto add_adapter_failed; + } + } + + dev_info(&pdev->dev, "16 port mux on %s adapter\n", parent->name); + + platform_set_drvdata(pdev, mux); + + return 0; + +add_adapter_failed: + for (; i > 0; i--) + i2c_del_mux_adapter(mux->child[i - 1]); + kfree(mux->child); +alloc_failed2: + kfree(mux); +alloc_failed: + i2c_put_adapter(parent); + + return ret; +} + +static int __exit qsfp_mux_remove(struct platform_device *pdev) +{ + int i; + struct qsfp_mux *mux = platform_get_drvdata(pdev); + + for (i = 0; i < QSFP_MODULE_NUM; i++) + i2c_del_mux_adapter(mux->child[i]); + + platform_set_drvdata(pdev, NULL); + i2c_put_adapter(mux->parent); + kfree(mux->child); + kfree(mux); + + return 0; +} + +static struct platform_driver qsfp_mux_driver = { + .probe = qsfp_mux_probe, + .remove = __exit_p(qsfp_mux_remove), /* TODO */ + .driver = { + .owner = THIS_MODULE, + .name = "dell-s6000-qsfp-mux", + }, +}; + +/* TODO */ +/* module_platform_driver */ + +static ssize_t get_modsel(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[slave_cpld].client, 0x0); + if (ret < 0) + return sprintf(buf, "na"); + data = (u32)ret & 0xff; + + ret = i2c_smbus_read_byte_data(pdata[slave_cpld].client, 0x1); + if (ret < 0) + return sprintf(buf, "na"); + data |= (u32)(ret & 0xff) << 8; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0xa); + if (ret < 0) + return sprintf(buf, "na"); + data |= (u32)(ret & 0xff) << 16; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0xb); + if (ret < 0) + return sprintf(buf, "na"); + data |= (u32)(ret & 0xff) << 24; + + return sprintf(buf, "0x%08x\n", data); +} + +static ssize_t get_lpmode(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[slave_cpld].client, 0x2); + if (ret < 0) + return sprintf(buf, "na"); + data = (u32)ret & 0xff; + + ret = i2c_smbus_read_byte_data(pdata[slave_cpld].client, 0x3); + if (ret < 0) + return sprintf(buf, "na"); + data |= (u32)(ret & 0xff) << 8; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0xc); + if (ret < 0) + return sprintf(buf, "na"); + data |= (u32)(ret & 0xff) << 16; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0xd); + if (ret < 0) + return sprintf(buf, "na"); + data |= (u32)(ret & 0xff) << 24; + + return sprintf(buf, "0x%08x\n", data); +} + +static ssize_t set_lpmode(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long data; + int err; + struct cpld_platform_data *pdata = dev->platform_data; + + err = kstrtoul(buf, 16, &data); + if (err) + return err; + + i2c_smbus_write_byte_data(pdata[slave_cpld].client, 0x2, (u8)(data & 0xff)); + i2c_smbus_write_byte_data(pdata[slave_cpld].client, 0x3, (u8)((data >> 8) & 0xff)); + i2c_smbus_write_byte_data(pdata[master_cpld].client, 0xc, (u8)((data >> 16) & 0xff)); + i2c_smbus_write_byte_data(pdata[master_cpld].client, 0xd, (u8)((data >> 24) & 0xff)); + + return count; +} + +static ssize_t get_reset(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[slave_cpld].client, 0x6); + if (ret < 0) + return sprintf(buf, "na"); + data = (u32)ret & 0xff; + + ret = i2c_smbus_read_byte_data(pdata[slave_cpld].client, 0x7); + if (ret < 0) + return sprintf(buf, "na"); + data |= (u32)(ret & 0xff) << 8; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x10); + if (ret < 0) + return sprintf(buf, "na"); + data |= (u32)(ret & 0xff) << 16; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x11); + if (ret < 0) + return sprintf(buf, "na"); + data |= (u32)(ret & 0xff) << 24; + + return sprintf(buf, "0x%08x\n", data); +} + +static ssize_t set_reset(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long data; + int err; + struct cpld_platform_data *pdata = dev->platform_data; + + err = kstrtoul(buf, 16, &data); + if (err) + return err; + + i2c_smbus_write_byte_data(pdata[slave_cpld].client, 0x6, (u8)(data & 0xff)); + i2c_smbus_write_byte_data(pdata[slave_cpld].client, 0x7, (u8)((data >> 8)& 0xff)); + i2c_smbus_write_byte_data(pdata[master_cpld].client, 0x10, (u8)((data >> 16) & 0xff)); + i2c_smbus_write_byte_data(pdata[master_cpld].client, 0x11, (u8)((data >> 24) & 0xff)); + + return count; +} + +static ssize_t get_modprs(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[slave_cpld].client, 0x4); + if (ret < 0) + return sprintf(buf, "read error"); + data = (u32)ret & 0xff; + + ret = i2c_smbus_read_byte_data(pdata[slave_cpld].client, 0x5); + if (ret < 0) + return sprintf(buf, "read error"); + data |= (u32)(ret & 0xff) << 8; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0xe); + if (ret < 0) + return sprintf(buf, "na"); + data |= (u32)(ret & 0xff) << 16; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0xf); + if (ret < 0) + return sprintf(buf, "na"); + data |= (u32)(ret & 0xff) << 24; + + return sprintf(buf, "0x%08x\n", data); +} + +static ssize_t set_power_reset(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long data; + int err; + struct cpld_platform_data *pdata = dev->platform_data; + + err = kstrtoul(buf, 10, &data); + if (err) + return err; + + if (data) + { + i2c_smbus_write_byte_data(pdata[system_cpld].client, 0x1, (u8)(0xfd)); + } + + return count; +} + +static ssize_t get_power_reset(struct device *dev, struct device_attribute *devattr, char *buf) +{ + return sprintf(buf, "0\n"); +} + +static ssize_t get_fan_prs(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x8); + if (ret < 0) + return sprintf(buf, "read error"); + data = (u32)((ret & 0xc0) >> 6); + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x9); + if (ret < 0) + return sprintf(buf, "read error"); + data |= (u32)((ret & 0x01) << 2); + data = ~data & 0x7; + + return sprintf(buf, "0x%x\n", data); +} + +static ssize_t get_psu0_prs(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x3); + if (ret < 0) + return sprintf(buf, "read error"); + + if (!(ret & 0x80)) + data = 1; + + return sprintf(buf, "%d\n", data); +} + +static ssize_t get_psu1_prs(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x3); + if (ret < 0) + return sprintf(buf, "read error"); + + if (!(ret & 0x08)) + data = 1; + + return sprintf(buf, "%d\n", data); +} + +static ssize_t get_psu0_status(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x3); + if (ret < 0) + return sprintf(buf, "read error"); + + if (!(ret & 0x40)) + data = 1; + + return sprintf(buf, "%d\n", data); +} + +static ssize_t get_psu1_status(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x3); + if (ret < 0) + return sprintf(buf, "read error"); + + if (!(ret & 0x04)) + data = 1; + + return sprintf(buf, "%d\n", data); +} + +static ssize_t get_system_led(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x7); + if (ret < 0) + return sprintf(buf, "read error"); + + data = (u32)(ret & 0x60) >> 5; + + switch (data) + { + case 0: + ret = sprintf(buf, "blink_green\n"); + break; + case 1: + ret = sprintf(buf, "green\n"); + break; + case 2: + ret = sprintf(buf, "yellow\n"); + break; + default: + ret = sprintf(buf, "blink_yellow\n"); + } + + return ret; +} + +static ssize_t set_system_led(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + if (!strncmp(buf, "blink_green", 11)) + { + data = 0; + } + else if (!strncmp(buf, "green", 5)) + { + data = 1; + } + else if (!strncmp(buf, "yellow", 6)) + { + data = 2; + } + else if (!strncmp(buf, "blink_yellow", 12)) + { + data = 3; + } + else + { + return -1; + } + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x7); + if (ret < 0) + return ret; + + i2c_smbus_write_byte_data(pdata[master_cpld].client, 0x7, (u8)((ret & 0x9F) | (data << 5))); + + return count; +} + +static ssize_t get_locator_led(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x7); + if (ret < 0) + return sprintf(buf, "read error"); + + data = (u32)(ret & 0x18) >> 3; + + switch (data) + { + case 0: + ret = sprintf(buf, "off\n"); + break; + case 1: + ret = sprintf(buf, "blink_blue\n"); + break; + case 2: + ret = sprintf(buf, "blue\n"); + break; + default: + ret = sprintf(buf, "invalid\n"); + } + + return ret; +} + +static ssize_t set_locator_led(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + if (!strncmp(buf, "off", 3)) + { + data = 0; + } + else if (!strncmp(buf, "blink_blue", 10)) + { + data = 1; + } + else if (!strncmp(buf, "blue", 4)) + { + data = 2; + } + else + { + return -1; + } + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x7); + if (ret < 0) + return ret; + + i2c_smbus_write_byte_data(pdata[master_cpld].client, 0x7, (u8)((ret & 0xE7) | (data << 3))); + + return count; +} + +static ssize_t get_power_led(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x7); + if (ret < 0) + return sprintf(buf, "read error"); + + data = (u32)(ret & 0x06) >> 1; + + switch (data) + { + case 0: + ret = sprintf(buf, "off\n"); + break; + case 1: + ret = sprintf(buf, "yellow\n"); + break; + case 2: + ret = sprintf(buf, "green\n"); + break; + default: + ret = sprintf(buf, "blink_yellow\n"); + } + + return ret; +} + +static ssize_t set_power_led(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + if (!strncmp(buf, "off", 3)) + { + data = 0; + } + else if (!strncmp(buf, "yellow", 6)) + { + data = 1; + } + else if (!strncmp(buf, "green", 5)) + { + data = 2; + } + else if (!strncmp(buf, "blink_yellow", 12)) + { + data = 3; + } + else + { + return -1; + } + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x7); + if (ret < 0) + return ret; + + i2c_smbus_write_byte_data(pdata[master_cpld].client, 0x7, (u8)((ret & 0xF9) | (data << 1))); + + return count; +} + +static ssize_t get_master_led(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x7); + if (ret < 0) + return sprintf(buf, "read error"); + + data = (u32)(ret & 0x1); + + switch (data) + { + case 0: + ret = sprintf(buf, "green\n"); + break; + default: + ret = sprintf(buf, "off\n"); + break; + } + + return ret; +} + +static ssize_t set_master_led(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + if (!strncmp(buf, "green", 5)) + { + data = 0; + } + else if (!strncmp(buf, "off", 3)) + { + data = 1; + } + else + { + return -1; + } + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x7); + if (ret < 0) + return ret; + + i2c_smbus_write_byte_data(pdata[master_cpld].client, 0x7, (u8)((ret & 0xFE) | data)); + + return count; +} + +static ssize_t get_fan_led(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x9); + if (ret < 0) + return sprintf(buf, "read error"); + + data = (u32)(ret & 0x18) >> 3; + + switch (data) + { + case 0: + ret = sprintf(buf, "off\n"); + break; + case 1: + ret = sprintf(buf, "yellow\n"); + break; + case 2: + ret = sprintf(buf, "green\n"); + break; + default: + ret = sprintf(buf, "blink_yellow\n"); + } + + return ret; +} + +static ssize_t set_fan_led(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + if (!strncmp(buf, "off", 3)) + { + data = 0; + } + else if (!strncmp(buf, "yellow", 6)) + { + data = 1; + } + else if (!strncmp(buf, "green", 5)) + { + data = 2; + } + else if (!strncmp(buf, "blink_yellow", 12)) + { + data = 3; + } + else + { + return -1; + } + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x9); + if (ret < 0) + return ret; + + i2c_smbus_write_byte_data(pdata[master_cpld].client, 0x9, (u8)((ret & 0xE7) | (data << 3))); + + return count; +} + +static ssize_t get_fan0_led(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x8); + if (ret < 0) + return sprintf(buf, "read error"); + + data = (u32)(ret & 0x3); + + switch (data) + { + case 0: + ret = sprintf(buf, "off\n"); + break; + case 1: + ret = sprintf(buf, "green\n"); + break; + case 2: + ret = sprintf(buf, "yellow\n"); + break; + default: + ret = sprintf(buf, "unknown\n"); + } + + return ret; +} + +static ssize_t set_fan0_led(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + if (!strncmp(buf, "off", 3)) + { + data = 0; + } + else if (!strncmp(buf, "yellow", 6)) + { + data = 2; + } + else if (!strncmp(buf, "green", 5)) + { + data = 1; + } + else + { + return -1; + } + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x8); + if (ret < 0) + return ret; + + i2c_smbus_write_byte_data(pdata[master_cpld].client, 0x8, (u8)((ret & 0xFC) | data)); + + return count; +} + + +static ssize_t get_fan1_led(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x8); + if (ret < 0) + return sprintf(buf, "read error"); + + data = (u32)(ret & 0xc) >> 2; + + switch (data) + { + case 0: + ret = sprintf(buf, "off\n"); + break; + case 1: + ret = sprintf(buf, "green\n"); + break; + case 2: + ret = sprintf(buf, "yellow\n"); + break; + default: + ret = sprintf(buf, "unknown\n"); + } + + return ret; +} + +static ssize_t set_fan1_led(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + if (!strncmp(buf, "off", 3)) + { + data = 0; + } + else if (!strncmp(buf, "yellow", 6)) + { + data = 2; + } + else if (!strncmp(buf, "green", 5)) + { + data = 1; + } + else + { + return -1; + } + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x8); + if (ret < 0) + return ret; + + i2c_smbus_write_byte_data(pdata[master_cpld].client, 0x8, (u8)((ret & 0xF3) | (data << 2))); + + return count; +} + +static ssize_t get_fan2_led(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x8); + if (ret < 0) + return sprintf(buf, "read error"); + + data = (u32)(ret & 0x30) >> 4; + + switch (data) + { + case 0: + ret = sprintf(buf, "off\n"); + break; + case 1: + ret = sprintf(buf, "green\n"); + break; + case 2: + ret = sprintf(buf, "yellow\n"); + break; + default: + ret = sprintf(buf, "unknown\n"); + } + + return ret; +} + +static ssize_t set_fan2_led(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + int ret; + u32 data = 0; + struct cpld_platform_data *pdata = dev->platform_data; + + if (!strncmp(buf, "off", 3)) + { + data = 0; + } + else if (!strncmp(buf, "yellow", 6)) + { + data = 2; + } + else if (!strncmp(buf, "green", 5)) + { + data = 1; + } + else + { + return -1; + } + + ret = i2c_smbus_read_byte_data(pdata[master_cpld].client, 0x8); + if (ret < 0) + return ret; + + i2c_smbus_write_byte_data(pdata[master_cpld].client, 0x8, (u8)((ret & 0xCF) | (data << 4))); + + return count; +} + +static DEVICE_ATTR(qsfp_modsel, S_IRUGO, get_modsel, NULL); +static DEVICE_ATTR(qsfp_modprs, S_IRUGO, get_modprs, NULL); +static DEVICE_ATTR(qsfp_lpmode, S_IRUGO | S_IWUSR, get_lpmode, set_lpmode); +static DEVICE_ATTR(qsfp_reset, S_IRUGO | S_IWUSR, get_reset, set_reset); +static DEVICE_ATTR(power_reset, S_IRUGO | S_IWUSR, get_power_reset, set_power_reset); +static DEVICE_ATTR(fan_prs, S_IRUGO, get_fan_prs, NULL); +static DEVICE_ATTR(psu0_prs, S_IRUGO, get_psu0_prs, NULL); +static DEVICE_ATTR(psu1_prs, S_IRUGO, get_psu1_prs, NULL); +static DEVICE_ATTR(psu0_status, S_IRUGO, get_psu0_status, NULL); +static DEVICE_ATTR(psu1_status, S_IRUGO, get_psu1_status, NULL); +static DEVICE_ATTR(system_led, S_IRUGO | S_IWUSR, get_system_led, set_system_led); +static DEVICE_ATTR(locator_led, S_IRUGO | S_IWUSR, get_locator_led, set_locator_led); +static DEVICE_ATTR(power_led, S_IRUGO | S_IWUSR, get_power_led, set_power_led); +static DEVICE_ATTR(master_led, S_IRUGO | S_IWUSR, get_master_led, set_master_led); +static DEVICE_ATTR(fan_led, S_IRUGO | S_IWUSR, get_fan_led, set_fan_led); +static DEVICE_ATTR(fan0_led, S_IRUGO | S_IWUSR, get_fan0_led, set_fan0_led); +static DEVICE_ATTR(fan1_led, S_IRUGO | S_IWUSR, get_fan1_led, set_fan1_led); +static DEVICE_ATTR(fan2_led, S_IRUGO | S_IWUSR, get_fan2_led, set_fan2_led); + +static struct attribute *s6000_cpld_attrs[] = { + &dev_attr_qsfp_modsel.attr, + &dev_attr_qsfp_lpmode.attr, + &dev_attr_qsfp_reset.attr, + &dev_attr_qsfp_modprs.attr, + &dev_attr_power_reset.attr, + &dev_attr_fan_prs.attr, + &dev_attr_psu0_prs.attr, + &dev_attr_psu1_prs.attr, + &dev_attr_psu0_status.attr, + &dev_attr_psu1_status.attr, + &dev_attr_system_led.attr, + &dev_attr_locator_led.attr, + &dev_attr_power_led.attr, + &dev_attr_master_led.attr, + &dev_attr_fan_led.attr, + &dev_attr_fan0_led.attr, + &dev_attr_fan1_led.attr, + &dev_attr_fan2_led.attr, + NULL, +}; + +static struct attribute_group s6000_cpld_attr_grp = { + .attrs = s6000_cpld_attrs, +}; + +static int __init cpld_probe(struct platform_device *pdev) +{ + struct cpld_platform_data *pdata; + struct i2c_adapter *parent; + int i; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -ENODEV; + } + + parent = i2c_get_adapter(S6000_MUX_BASE_NR); + if (!parent) { + printk(KERN_WARNING "Parent adapter (%d) not found\n", + S6000_MUX_BASE_NR); + return -ENODEV; + } + + for (i = 0; i < CPLD_DEVICE_NUM; i++) { + pdata[i].client = i2c_new_dummy(parent, pdata[i].reg_addr); + if (!pdata[i].client) { + printk(KERN_WARNING "Fail to create dummy i2c client for addr %d\n", pdata[i].reg_addr); + goto error; + } + } + + ret = sysfs_create_group(&pdev->dev.kobj, &s6000_cpld_attr_grp); + if (ret) + goto error; + + return 0; + +error: + i--; + for (; i >= 0; i--) { + if (pdata[i].client) { + i2c_unregister_device(pdata[i].client); + } + } + + i2c_put_adapter(parent); + + return -ENODEV; +} + +static int __exit cpld_remove(struct platform_device *pdev) +{ + int i; + struct i2c_adapter *parent = NULL; + struct cpld_platform_data *pdata = pdev->dev.platform_data; + + sysfs_remove_group(&pdev->dev.kobj, &s6000_cpld_attr_grp); + + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + } else { + for (i = 0; i < CPLD_DEVICE_NUM; i++) { + if (pdata[i].client) { + if (!parent) { + parent = (pdata[i].client)->adapter; + } + i2c_unregister_device(pdata[i].client); + } + } + } + + i2c_put_adapter(parent); + + return 0; +} + +static struct platform_driver cpld_driver = { + .probe = cpld_probe, + .remove = __exit_p(cpld_remove), + .driver = { + .owner = THIS_MODULE, + .name = "dell-s6000-cpld", + }, +}; + +static int __init i2c_device_probe(struct platform_device *pdev) +{ + struct i2c_device_platform_data *pdata; + struct i2c_adapter *parent; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -ENODEV; + } + + parent = i2c_get_adapter(pdata->parent); + if (!parent) { + dev_err(&pdev->dev, "Parent adapter (%d) not found\n", + pdata->parent); + return -ENODEV; + } + + pdata->client = i2c_new_device(parent, &pdata->info); + if (!pdata->client) { + dev_err(&pdev->dev, "Failed to create i2c client %s at %d\n", + pdata->info.type, pdata->parent); + return -ENODEV; + } + + return 0; +} + +static int __exit i2c_deivce_remove(struct platform_device *pdev) +{ + struct i2c_adapter *parent; + struct i2c_device_platform_data *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -ENODEV; + } + + if (pdata->client) { + parent = i2c_get_adapter(pdata->parent); + i2c_unregister_device(pdata->client); + i2c_put_adapter(parent); + } + + return 0; +} + + +static struct platform_driver i2c_device_driver = { + .probe = i2c_device_probe, + .remove = __exit_p(i2c_deivce_remove), + .driver = { + .owner = THIS_MODULE, + .name = "dell-s6000-i2c-device", + } +}; + +static int __init dell_s6000_platform_init(void) +{ + int ret = 0; + struct cpld_platform_data *cpld_pdata; + struct qsfp_mux_platform_data *qsfp_pdata; + int i; + + printk("delll_s6000_platform module initialization\n"); + + mdelay(10000); + + ret = platform_driver_register(&cpld_driver); + if (ret) { + printk(KERN_WARNING "Fail to register cpld driver\n"); + goto error_cpld_driver; + } + + ret = platform_driver_register(&qsfp_mux_driver); + if (ret) { + printk(KERN_WARNING "Fail to register qsfp mux driver\n"); + goto error_qsfp_mux_driver; + } + + ret = platform_driver_register(&i2c_device_driver); + if (ret) { + printk(KERN_WARNING "Fail to register i2c device driver\n"); + goto error_i2c_device_driver; + } + + ret = platform_device_register(&s6000_mux); + if (ret) { + printk(KERN_WARNING "Fail to create gpio mux\n"); + goto error_mux; + } + + ret = platform_device_register(&s6000_cpld); + if (ret) { + printk(KERN_WARNING "Fail to create cpld device\n"); + goto error_cpld; + } + + cpld_pdata = s6000_cpld.dev.platform_data; + qsfp_pdata = s6000_qsfp_mux[0].dev.platform_data; + qsfp_pdata->cpld = cpld_pdata[slave_cpld].client; + qsfp_pdata = s6000_qsfp_mux[1].dev.platform_data; + qsfp_pdata->cpld = cpld_pdata[master_cpld].client; + + for (i = 0; i < QSFP_DEVICE_NUM; i++) { + ret = platform_device_register(&s6000_qsfp_mux[i]); + if (ret) { + printk(KERN_WARNING "fail to create qsfp mux %d\n", i); + goto error_qsfp_mux; + } + } + + for (i = 0; i < ARRAY_SIZE(s6000_i2c_device_platform_data); i++) { + ret = platform_device_register(&s6000_i2c_device[i]); + if (ret) { + printk(KERN_WARNING "fail to create qsfp mux %d\n", i); + goto error_i2c_device; + } + } + + if (ret) + goto error_qsfp_mux; + + return 0; + +error_i2c_device: + i--; + for (; i >= 0; i--) { + platform_device_unregister(&s6000_i2c_device[i]); + } + i = QSFP_DEVICE_NUM; +error_qsfp_mux: + i--; + for (; i >= 0; i--) { + platform_device_unregister(&s6000_qsfp_mux[i]); + } + platform_device_unregister(&s6000_cpld); +error_cpld: + platform_device_unregister(&s6000_mux); +error_mux: + platform_driver_unregister(&i2c_device_driver); +error_i2c_device_driver: + platform_driver_unregister(&qsfp_mux_driver); +error_qsfp_mux_driver: + platform_driver_unregister(&cpld_driver); +error_cpld_driver: + return ret; +} + +static void __exit dell_s6000_platform_exit(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s6000_i2c_device_platform_data); i++) + platform_device_unregister(&s6000_i2c_device[i]); + for (i = 0; i < MUX_CHANNEL_NUM; i++) + platform_device_unregister(&s6000_qsfp_mux[i]); + platform_device_unregister(&s6000_cpld); + platform_device_unregister(&s6000_mux); + + platform_driver_unregister(&i2c_device_driver); + platform_driver_unregister(&cpld_driver); + platform_driver_unregister(&qsfp_mux_driver); +} + +module_init(dell_s6000_platform_init); +module_exit(dell_s6000_platform_exit); + +MODULE_DESCRIPTION("DELL S6000 Platform Support"); +MODULE_AUTHOR("Guohan Lu "); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-s6000/scripts/reset-qsfp b/platform/broadcom/sonic-platform-modules-s6000/scripts/reset-qsfp new file mode 100755 index 000000000000..6b017ed37c26 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/scripts/reset-qsfp @@ -0,0 +1,25 @@ +#!/bin/bash + +# Usage: +# Reset QSFP manually by writing 1/0 to QSFP reset pin + +QSFP_RESET=/sys/bus/platform/devices/dell-s6000-cpld.0/qsfp_reset + +logger -t platform-modules "Reset QSFP modules" + +# Retry three times +for i in `seq 1 3` +do + if [ -w $QSFP_RESET ]; then + echo 0x00000000 > $QSFP_RESET + # Sleep 1 second to reset QSFP + sleep 1 + echo 0xffffffff > $QSFP_RESET + exit 0 + fi + # Sleep for 3 seconds to wait for device tree to be ready + sleep 3 +done + +logger -p user.error -t platform-modules "Failed to reset QSFP modules!" +exit 1 diff --git a/platform/broadcom/sonic-platform-modules-s6000/scripts/set-fan-speed b/platform/broadcom/sonic-platform-modules-s6000/scripts/set-fan-speed new file mode 100755 index 000000000000..00c738f23a8d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-s6000/scripts/set-fan-speed @@ -0,0 +1,58 @@ +#!/bin/bash + +# Usage: +# Set all fans speed to $1 + +usage() { + echo "This script must be run with super-user privilege." + echo "Warning! Wrongly set fan speed may result in physical damage to the device." + echo "" + echo "usage: set-fan-speed speed_in_rpm" + echo "example: set-fan-speed 15000" +} + +if [ $# -ne 1 ]; then + usage + exit 1 +fi + +PSU_FAN1=/sys/class/i2c-adapter/i2c-1/1-0058/fan1_target +PSU_FAN2=/sys/class/i2c-adapter/i2c-1/1-0059/fan1_target + +# Three fan trays with each contains two separate fans +# fan1-fan4 fan2-fan5 fan3-fan6 +FAN1=/sys/class/i2c-adapter/i2c-11/11-0029/fan1_target +FAN2=/sys/class/i2c-adapter/i2c-11/11-0029/fan2_target +FAN3=/sys/class/i2c-adapter/i2c-11/11-0029/fan3_target +FAN4=/sys/class/i2c-adapter/i2c-11/11-0029/fan4_target +FAN5=/sys/class/i2c-adapter/i2c-11/11-002a/fan1_target +FAN6=/sys/class/i2c-adapter/i2c-11/11-002a/fan2_target + +speed=$1 +logger -t platform-modules "Trying to set fan speed to $speed" + +# Retry three times +for i in `seq 1 3` +do + if [ -w $FAN1 -o -w $FAN2 -o -w $FAN3 ]; then + # set default psu fan speed + echo $speed > $PSU_FAN1 + echo $speed > $PSU_FAN2 + # set default fan speed + echo $speed > $FAN1 + echo $speed > $FAN2 + echo $speed > $FAN3 + echo $speed > $FAN4 + echo $speed > $FAN5 + echo $speed > $FAN6 + + logger -t platform-modules "Fan speed is set to $speed" + + exit 0 + fi + # Sleep for 3 seconds to wait for device tree to be ready + sleep 3 +done + +logger -p user.error -t platform-modules "Failed to set fan speed!" +exit 1 diff --git a/platform/nephos/sonic-platform-modules-ingrasys b/platform/nephos/sonic-platform-modules-ingrasys deleted file mode 160000 index 796169e43aee..000000000000 --- a/platform/nephos/sonic-platform-modules-ingrasys +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 796169e43aee944fdf7b549d04cb181117e0fb89 diff --git a/platform/nephos/sonic-platform-modules-ingrasys/.gitignore b/platform/nephos/sonic-platform-modules-ingrasys/.gitignore new file mode 100644 index 000000000000..c6127b38c1aa --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/platform/nephos/sonic-platform-modules-ingrasys/LICENSE b/platform/nephos/sonic-platform-modules-ingrasys/LICENSE new file mode 100644 index 000000000000..9cecc1d4669e --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/changelog b/platform/nephos/sonic-platform-modules-ingrasys/debian/changelog new file mode 100644 index 000000000000..a3e3f92cc731 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/changelog @@ -0,0 +1,11 @@ +platform-driver (1.0.0) unstable; urgency=low + + * Add support for S9130-32X, S9230-64X series + + -- developer Wed, 29 Mar 2017 11:00:00 +0800 + +platform-driver (1.0.0) unstable; urgency=low + + * Initial release (Closes: #nnnn) + + -- developer Wed, 05 Oct 2016 16:30:45 +0800 diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/compat b/platform/nephos/sonic-platform-modules-ingrasys/debian/compat new file mode 100644 index 000000000000..ec635144f600 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/compat @@ -0,0 +1 @@ +9 diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/control b/platform/nephos/sonic-platform-modules-ingrasys/debian/control new file mode 100644 index 000000000000..3db0b5f3505f --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/control @@ -0,0 +1,15 @@ +Source: platform-driver +Section: unknown +Priority: optional +Maintainer: Wade He +Build-Depends: debhelper (>= 9), dh-systemd +Standards-Version: 1.0.0 + +Package: sonic-platform-ingrasys-s9130-32x +Architecture: amd64 +Description: This package contains S9130-32X platform driver utility for SONiC project. + +Package: sonic-platform-ingrasys-s9230-64x +Architecture: amd64 +Description: This package contains S9230-64X platform driver utility for SONiC project. + diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/rules b/platform/nephos/sonic-platform-modules-ingrasys/debian/rules new file mode 100644 index 000000000000..4ca2c6ac9b87 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/rules @@ -0,0 +1,82 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +export INSTALL_MOD_DIR:=extra + +PACKAGE_PRE_NAME := sonic-platform-ingrasys +KVERSION ?= $(shell uname -r) +KERNEL_SRC := /lib/modules/$(KVERSION) +MOD_SRC_DIR:= $(shell pwd) +MODULE_DIRS:= s9130-32x s9230-64x +MODULE_DIR := modules +UTILS_DIR := utils +SERVICE_DIR := service +CONF_DIR := conf + +%: + dh $@ --with systemd + +clean: + dh_testdir + dh_testroot + dh_clean + +build: + #make modules -C $(KERNEL_SRC)/build M=$(MODULE_SRC) + (for mod in $(MODULE_DIRS); do \ + make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + done) + +binary: binary-arch binary-indep + # Nothing to do + +binary-arch: + # Nothing to do + +#install: build + #dh_testdir + #dh_testroot + #dh_clean -k + #dh_installdirs + +binary-indep: + dh_testdir + dh_installdirs + + # Custom package commands + (for mod in $(MODULE_DIRS); do \ + mkdir debian/tmp/usr/sbin -p; \ + mkdir debian/tmp/lib/systemd/system -p; \ + mkdir debian/tmp/etc -p; \ + dh_installdirs -p$(PACKAGE_PRE_NAME)-$${mod} $(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$${mod}/$(MODULE_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/$${mod}/$(UTILS_DIR)/*.sh debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/sbin/; \ + cp $(MOD_SRC_DIR)/$${mod}/$(UTILS_DIR)/*.ctrl debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/sbin/; \ + cp $(MOD_SRC_DIR)/$${mod}/$(UTILS_DIR)/fancontrol debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/sbin/; \ + cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/*.service debian/$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system/; \ + done) + # Resuming debhelper scripts + dh_testroot + dh_install + dh_installchangelogs + dh_installdocs + dh_systemd_enable + dh_installinit + dh_systemd_start + dh_link + dh_fixperms + dh_compress + dh_strip + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb +.PHONY: build binary binary-arch binary-indep clean diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.dirs b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.dirs new file mode 100644 index 000000000000..bec29f195770 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.dirs @@ -0,0 +1,3 @@ +usr/sbin +lib/systemd/system +etc/ \ No newline at end of file diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.install b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.install new file mode 100644 index 000000000000..38efbef5d864 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.install @@ -0,0 +1,3 @@ +lib/systemd/ +usr/sbin/ +etc/ \ No newline at end of file diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.links b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.links new file mode 100644 index 000000000000..4e59c7e191e4 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.links @@ -0,0 +1 @@ +/usr/share/sonic/device/x86_64-ingrasys_s9130_32x-r0/fancontrol /etc/fancontrol diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.postinst b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.postinst new file mode 100644 index 000000000000..504511bd72cc --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.postinst @@ -0,0 +1,44 @@ +# Automatically added by dh_systemd_enable +# This will only remove masks created by d-s-h on package removal. +deb-systemd-helper unmask s9130-32x-monitor.service >/dev/null || true +deb-systemd-helper unmask fancontrol.service >/dev/null || true +deb-systemd-helper unmask qsfp-monitor.service >/dev/null || true +# Generate kernel modules.dep and map files for add eeprom_mb. +depmod -a +# was-enabled defaults to true, so new installations run enable. +if deb-systemd-helper --quiet was-enabled s9130-32x-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable s9130-32x-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state s9130-32x-monitor.service >/dev/null || true +fi +if deb-systemd-helper --quiet was-enabled qsfp-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable qsfp-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state qsfp-monitor.service >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s9130-32x-monitor" ]; then + update-rc.d s9130-32x-monitor defaults >/dev/null + invoke-rc.d s9130-32x-monitor start || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + update-rc.d qsfp-monitor defaults >/dev/null + invoke-rc.d qsfp-monitor start || exit $? +fi +# End automatically added section +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + deb-systemd-invoke start s9130-32x-monitor.service >/dev/null || true + deb-systemd-invoke start qsfp-monitor.service >/dev/null || true +fi +# End automatically added section diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.postrm b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.postrm new file mode 100644 index 000000000000..bc4de635053e --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.postrm @@ -0,0 +1,38 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + fi +# End automatically added section +# Automatically added by dh_installinit +if [ "$1" = "purge" ] ; then + update-rc.d s9130-32x-monitor remove >/dev/null + update-rc.d qsfp-monitor remove >/dev/null +fi + + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [ -d /run/systemd/system ] ; then + systemctl --system daemon-reload >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_systemd_enable +if [ "$1" = "remove" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper mask s9130-32x-monitor.service >/dev/null + deb-systemd-helper mask qsfp-monitor.service >/dev/null + fi +fi + +if [ "$1" = "purge" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper purge s9130-32x-monitor.service >/dev/null + deb-systemd-helper unmask s9130-32x-monitor.service >/dev/null + deb-systemd-helper purge qsfp-monitor.service >/dev/null + deb-systemd-helper unmask qsfp-monitor.service >/dev/null + fi +fi +# Generate kernel modules.dep and map files for remove eeprom_mb. +depmod -a || true +# End automatically added section + diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.prerm b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.prerm new file mode 100644 index 000000000000..a5271f1faf68 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.prerm @@ -0,0 +1,17 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + deb-systemd-invoke stop s9130-32x-monitor.service >/dev/null + deb-systemd-invoke stop qsfp-monitor.service >/dev/null +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s9130-32x-monitor" ]; then + invoke-rc.d s9130-32x-monitor stop || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + invoke-rc.d qsfp-monitor stop || exit $? +fi +# Driver deinit +/usr/sbin/i2c_utils.sh i2c_deinit +# End automatically added section + diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.upstart b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.upstart new file mode 100644 index 000000000000..bda8c7661c34 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9130-32x.upstart @@ -0,0 +1,6 @@ +description "SONiC platform service" + +respawn + +exec /usr/sbin/s9130_32x_monitor.sh +exec /usr/sbin/qsfp_monitor.sh diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.dirs b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.dirs new file mode 100644 index 000000000000..bec29f195770 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.dirs @@ -0,0 +1,3 @@ +usr/sbin +lib/systemd/system +etc/ \ No newline at end of file diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.install b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.install new file mode 100644 index 000000000000..38efbef5d864 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.install @@ -0,0 +1,3 @@ +lib/systemd/ +usr/sbin/ +etc/ \ No newline at end of file diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.links b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.links new file mode 100644 index 000000000000..e5b8a45693d0 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.links @@ -0,0 +1 @@ +/usr/share/sonic/device/x86_64-ingrasys_s9230_64x-r0/fancontrol /etc/fancontrol diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.postinst b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.postinst new file mode 100644 index 000000000000..ebba922f41de --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.postinst @@ -0,0 +1,45 @@ +# Automatically added by dh_systemd_enable +# This will only remove masks created by d-s-h on package removal. +deb-systemd-helper unmask s9230-64x-monitor.service >/dev/null || true +deb-systemd-helper unmask fancontrol.service >/dev/null || true +deb-systemd-helper unmask qsfp-monitor.service >/dev/null || true +# Generate kernel modules.dep and map files for add eeprom_mb +# and ingrasys_s9230_64x_i2c_cpld +depmod -a +# was-enabled defaults to true, so new installations run enable. +if deb-systemd-helper --quiet was-enabled s9230-64x-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable s9230-64x-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state s9230-64x-monitor.service >/dev/null || true +fi +if deb-systemd-helper --quiet was-enabled qsfp-monitor.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable qsfp-monitor.service >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state qsfp-monitor.service >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s9230-64x-monitor" ]; then + update-rc.d s9230-64x-monitor defaults >/dev/null + invoke-rc.d s9230-64x-monitor start || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + update-rc.d qsfp-monitor defaults >/dev/null + invoke-rc.d qsfp-monitor start || exit $? +fi +# End automatically added section +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + deb-systemd-invoke start s9230-64x-monitor.service >/dev/null || true + deb-systemd-invoke start qsfp-monitor.service >/dev/null || true +fi +# End automatically added section diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.postrm b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.postrm new file mode 100644 index 000000000000..36efe65f6bc1 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.postrm @@ -0,0 +1,38 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + fi +# End automatically added section +# Automatically added by dh_installinit +if [ "$1" = "purge" ] ; then + update-rc.d s9230-64x-monitor remove >/dev/null + update-rc.d qsfp-monitor remove >/dev/null +fi + + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [ -d /run/systemd/system ] ; then + systemctl --system daemon-reload >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_systemd_enable +if [ "$1" = "remove" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper mask s9230-64x-monitor.service >/dev/null + deb-systemd-helper mask qsfp-monitor.service >/dev/null + fi +fi + +if [ "$1" = "purge" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper purge s9230-64x-monitor.service >/dev/null + deb-systemd-helper unmask s9230-64x-monitor.service >/dev/null + deb-systemd-helper purge qsfp-monitor.service >/dev/null + deb-systemd-helper unmask qsfp-monitor.service >/dev/null + fi +fi +# Generate kernel modules.dep and map files for remove eeprom_mb. +depmod -a || true +# End automatically added section + diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.prerm b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.prerm new file mode 100644 index 000000000000..1c7ace21990e --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.prerm @@ -0,0 +1,17 @@ +# Automatically added by dh_systemd_start +if [ -d /run/systemd/system ]; then + deb-systemd-invoke stop s9230-64x-monitor.service >/dev/null + deb-systemd-invoke stop qsfp-monitor.service >/dev/null +fi +# End automatically added section +# Automatically added by dh_installinit +if [ -x "/etc/init.d/s9230-64x-monitor" ]; then + invoke-rc.d s9230-64x-monitor stop || exit $? +fi +if [ -x "/etc/init.d/qsfp-monitor" ]; then + invoke-rc.d qsfp-monitor stop || exit $? +fi +# Driver deinit +/usr/sbin/i2c_utils.sh i2c_deinit +# End automatically added section + diff --git a/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.upstart b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.upstart new file mode 100644 index 000000000000..6cbd9482f2d2 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/debian/sonic-platform-ingrasys-s9230-64x.upstart @@ -0,0 +1,6 @@ +description "SONiC platform service" + +respawn + +exec /usr/sbin/s9230_64x_monitor.sh +exec /usr/sbin/qsfp_monitor.sh diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/README.md b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/README.md new file mode 100644 index 000000000000..1b3b14db343b --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/README.md @@ -0,0 +1,167 @@ +# Ingrasys S9130-32X Platform Driver for SONiC + +Copyright (C) 2016 Ingrasys, Inc. + +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 3 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, see . + + +## Licensing terms + +The Licensing terms of the files within this project is split 2 parts. +The Linux kernel is released under the GNU General Public License version 2. +All other code is released under the GNU General Public License version 3. +Please see the LICENSE file for copies of both licenses. + +## Contents of this package + + - service/ + > Service config files for platform initialization and monitoring + - utils/ + > Scripts useful during platform bringup and sysfs function + - conf/ + > Platform configure files. + +## Kernel modules and drivers + +The driver for interacting with Ingrasys S9130-32X is contained in the I2C +kernel module and initialization script. The initialization script loads +the modules in the correct order. It has been built and tested against +the Linux 3.16 kernel. + +The initialization script will modprobe the needed modules, navigate to the +module's device directory in sysfs, and write configuration data to +the kernel module. + +### IGB + +This is OOB Ports on front panel for management plane. + +The IGB module must be loaded first on Ingrasys S9130-32X platform. + +### I2C i801 + +The I2C i801 on Ingrasys S9130-32X can be found in +`/sys/bus/i2c/devices/i2c-0/` + +This is I2C bus for power monitor, FAN controller, HWM and thermal sensors. + +### I2C PCA9548 +The PCA9548 module on S9130-32X can be found in +`/sys/bus/i2c/devices/i2c-1/` , `/sys/bus/i2c/devices/i2c-2/`, +`/sys/bus/i2c/devices/i2c-3/`, `/sys/bus/i2c/devices/i2c-4/`, +`/sys/bus/i2c/devices/i2c-5/`, `/sys/bus/i2c/devices/i2c-6/`, +`/sys/bus/i2c/devices/i2c-7/`, `/sys/bus/i2c/devices/i2c-8/` + +The pca9548 module for zQSFP module get/set functions, PSU information, +fan status and EEPROM. + +## Hardware components + +The hardware components are initialized in the init script on S9130-32X. +The following describes manual initialization as well as interaction. +The examples below are just for Ingrasys S9130-32X platform. + +### Hardware initialization + +When the sonic-platform-ingrasys-s9130-32x package is installed on the S9130-32X, +it is automatically initialized. If you want to manual initialization, the +utility command usage as follows: +``` + i2c_utils.sh i2c_init +``` + +### EEPROM + +The EEPROM is including the board SKU, model name, vendor name, serial number, +and other information can be accessed with the specific eeprom kernel module. +After using `modprobe eeprom_mb` to detect the eeprom, it will show up in sysfs. + +The hexdump utility can be used to decode the raw output of the EEPROM. +For example, +``` + bash# echo "mb_eeprom 0x51" > /sys/bus/i2c/devices/i2c-0/new_device + bash# cat /sys/bus/i2c/drivers/mb_eeprom/0-0051/eeprom | hexdump -C +``` + +### Front panel LEDs + +LEDs can be setup on/off by using i2c utility `/usr/sbin/i2c_utils.sh`. +Utility function command usage as follows: + +``` +Status LED: + i2c_utils.sh i2c_sys_led green|amber + +Fan status LED: + i2c_utils.sh i2c_fan_led green|amber on|off + +PSU1 status LED: + i2c_utils.sh i2c_psu1_led green|amber on|off + +PSU2 status LED: + i2c_utils.sh i2c_psu2_led green|amber on|off + +``` +QSFP Module Port LEDs control by ASIC library. + + +### Fan speed + +Fan speed are controlled by the w83795 kernel module. +It can be found in `/sys/class/hwmon/hwmon5/device/`. +If they were compiled as modules, you will need to modprobe w83795 for +their sysfs entries to show up. Each fan has an `fan_input` file +for reading the fan speed. And `pwm1` setting fan1 to fan4, +`pwm2` setting fan5 to fan8. + +There is docker-platform-monitor container installed fancontrol package that can +automatic control platform fan speed. + + +### Temperature sensors + +There is docker-platform-monitor container installed lm-sensors package that can +monitor platform temperature. And `sensors` command can show each +temperature sensors status. + +### Power supplies + +Power supplies status and its EEPROM info can be used i2c utility +`/usr/sbin/i2c_utils.sh` to get. +The usage as follows: +``` +PSU EEPROM: + i2c_utils.sh i2c_psu_eeprom_get + hexdump -C psu0.rom + hexdump -C psu1.rom + +PSU Status: + i2c_utils.sh i2c_psu_status +``` + +### QSFPs +QSFP modules are managed by the pca9548 kernel driver. +The i2c utility `/usr/sbin/i2c_utils.sh` can be used to get status and +module EEPROM informations. +The usage as follows: +``` +QSFP EEPROM: + i2c_utils.sh i2c_qsfp_eeprom_get [1-32] + +QSFP Insert Event: + i2c_utils.sh i2c_qsfp_eeprom_get [1-32] + 0 => not insert + 1 => inserted +``` + diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/modules/Makefile b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/modules/Makefile new file mode 100644 index 000000000000..688bb4666444 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/modules/Makefile @@ -0,0 +1,2 @@ +obj-m := eeprom_mb.o +obj-m += ingrasys_s9130_32x_psu.o diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/modules/eeprom_mb.c b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/modules/eeprom_mb.c new file mode 100644 index 000000000000..528864d93382 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/modules/eeprom_mb.c @@ -0,0 +1,267 @@ +/* + * Copyright (C) 1998, 1999 Frodo Looijaard and + * Philip Edelbrock + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003 IBM Corp. + * Copyright (C) 2004 Jean Delvare + * + * 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. + */ + +/* enable dev_dbg print out */ +//#define DEBUG + +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { /*0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57,*/ I2C_CLIENT_END }; + +/* Size of EEPROM in bytes */ +#define EEPROM_SIZE 512 + +#define SLICE_BITS (6) +#define SLICE_SIZE (1 << SLICE_BITS) +#define SLICE_NUM (EEPROM_SIZE/SLICE_SIZE) + +/* Each client has this additional data */ +struct eeprom_data { + struct mutex update_lock; + u8 valid; /* bitfield, bit!=0 if slice is valid */ + unsigned long last_updated[SLICE_NUM]; /* In jiffies, 8 slices */ + u8 data[EEPROM_SIZE]; /* Register values */ +}; + + +static void mb_eeprom_update_client(struct i2c_client *client, u8 slice) +{ + struct eeprom_data *data = i2c_get_clientdata(client); + int i, j; + int ret; + int addr; + + mutex_lock(&data->update_lock); + + if (!(data->valid & (1 << slice)) || + time_after(jiffies, data->last_updated[slice] + 300 * HZ)) { + dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice); + + addr = slice << SLICE_BITS; + + ret = i2c_smbus_write_byte_data(client, (u8)((addr >> 8) & 0xFF), (u8)(addr & 0xFF)); + /* select the eeprom address */ + if (ret < 0) { + dev_err(&client->dev, "address set failed\n"); + goto exit; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE)) { + goto exit; + } + + for (i = slice << SLICE_BITS; i < (slice + 1) << SLICE_BITS; i+= SLICE_SIZE) { + for (j = i; j < (i+SLICE_SIZE); j++) { + int res; + + res = i2c_smbus_read_byte(client); + if (res < 0) { + goto exit; + } + + data->data[j] = res & 0xFF; + } + } + + data->last_updated[slice] = jiffies; + data->valid |= (1 << slice); + } +exit: + mutex_unlock(&data->update_lock); +} + +static ssize_t mb_eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct eeprom_data *data = i2c_get_clientdata(client); + u8 slice; + + if (off > EEPROM_SIZE) { + return 0; + } + if (off + count > EEPROM_SIZE) { + count = EEPROM_SIZE - off; + } + if (count == 0) { + return 0; + } + + /* Only refresh slices which contain requested bytes */ + for (slice = off >> SLICE_BITS; slice <= (off + count - 1) >> SLICE_BITS; slice++) { + mb_eeprom_update_client(client, slice); + } + + memcpy(buf, &data->data[off], count); + + return count; +} + +static ssize_t mb_eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct eeprom_data *data = i2c_get_clientdata(client); + int ret; + int i; + u8 cmd; + u16 value16; + + dev_dbg(&client->dev, "mb_eeprom_write off=%d, count=%d\n", (int)off, (int)count); + + if (off > EEPROM_SIZE) { + return 0; + } + if (off + count > EEPROM_SIZE) { + count = EEPROM_SIZE - off; + } + if (count == 0) { + return 0; + } + + mutex_lock(&data->update_lock); + + for(i=0; i < count; i++) { + /* write command */ + cmd = (off >> 8) & 0xff; + value16 = off & 0xff; + value16 |= buf[i] << 8; + ret = i2c_smbus_write_word_data(client, cmd, value16); + + if (ret < 0) { + dev_err(&client->dev, "write address failed at %d \n", (int)off); + goto exit; + } + + off++; + + /* need to wait for write complete */ + udelay(10000); + } +exit: + mutex_unlock(&data->update_lock); + /* force to update client when reading */ + for(i=0; i < SLICE_NUM; i++) { + data->last_updated[i] = 0; + } + + return count; +} + +static struct bin_attribute mb_eeprom_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO | S_IWUSR, + }, + .size = EEPROM_SIZE, + .read = mb_eeprom_read, + .write = mb_eeprom_write, +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int mb_eeprom_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all + addresses 0x50-0x57, but we only care about 0x51 and 0x55. So decline + attaching to addresses >= 0x56 on DDC buses */ + if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x56) { + return -ENODEV; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE) + && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + return -ENODEV; + } + + strlcpy(info->type, "eeprom", I2C_NAME_SIZE); + + return 0; +} + +static int mb_eeprom_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct eeprom_data *data; + int err; + + if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + memset(data->data, 0xff, EEPROM_SIZE); + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &mb_eeprom_attr); + if (err) { + goto exit_kfree; + } + + return 0; + +exit_kfree: + kfree(data); +exit: + return err; +} + +static int mb_eeprom_remove(struct i2c_client *client) +{ + sysfs_remove_bin_file(&client->dev.kobj, &mb_eeprom_attr); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id mb_eeprom_id[] = { + { "mb_eeprom", 0 }, + { } +}; + +static struct i2c_driver mb_eeprom_driver = { + .driver = { + .name = "mb_eeprom", + }, + .probe = mb_eeprom_probe, + .remove = mb_eeprom_remove, + .id_table = mb_eeprom_id, + + .class = I2C_CLASS_DDC | I2C_CLASS_SPD, + .detect = mb_eeprom_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(mb_eeprom_driver); + +MODULE_AUTHOR("Wade "); +MODULE_DESCRIPTION("Ingrasys Mother Borad EEPROM driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/modules/ingrasys_s9130_32x_platform.h b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/modules/ingrasys_s9130_32x_platform.h new file mode 100644 index 000000000000..23dcf17a855b --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/modules/ingrasys_s9130_32x_platform.h @@ -0,0 +1,148 @@ +#ifndef _S9130_32X_PLATFORM_H +#define _S9130_32X_PLATFORM_H + +#include + +// remove debug before release +#define DEBUG + +enum bus_order { + I2C_BUS_MAIN, + MUX_9548_0_CH0, + MUX_9548_0_CH1, + MUX_9548_0_CH2, + MUX_9548_0_CH3, + MUX_9548_0_CH4, + MUX_9548_0_CH5, + MUX_9548_0_CH6, + MUX_9548_0_CH7, + MUX_9548_1_CH0, + MUX_9548_1_CH1, + MUX_9548_1_CH2, + MUX_9548_1_CH3, + MUX_9548_1_CH4, + MUX_9548_1_CH5, + MUX_9548_1_CH6, + MUX_9548_1_CH7, + MUX_9546_0_CH0, + MUX_9546_0_CH1, + MUX_9546_0_CH2, + MUX_9546_0_CH3, + MUX_9546_1_CH0, + MUX_9546_1_CH1, + MUX_9546_1_CH2, + MUX_9546_1_CH3, + MUX_9548_2_CH0, + MUX_9548_2_CH1, + MUX_9548_2_CH2, + MUX_9548_2_CH3, + MUX_9548_2_CH4, + MUX_9548_2_CH5, + MUX_9548_2_CH6, + MUX_9548_2_CH7, + MUX_9548_3_CH0, + MUX_9548_3_CH1, + MUX_9548_3_CH2, + MUX_9548_3_CH3, + MUX_9548_3_CH4, + MUX_9548_3_CH5, + MUX_9548_3_CH6, + MUX_9548_3_CH7, + MUX_9548_4_CH0, + MUX_9548_4_CH1, + MUX_9548_4_CH2, + MUX_9548_4_CH3, + MUX_9548_4_CH4, + MUX_9548_4_CH5, + MUX_9548_4_CH6, + MUX_9548_4_CH7, + MUX_9548_5_CH0, + MUX_9548_5_CH1, + MUX_9548_5_CH2, + MUX_9548_5_CH3, + MUX_9548_5_CH4, + MUX_9548_5_CH5, + MUX_9548_5_CH6, + MUX_9548_5_CH7, + MUX_9548_6_CH0, + MUX_9548_6_CH1, + MUX_9548_6_CH2, + MUX_9548_6_CH3, + MUX_9548_6_CH4, + MUX_9548_6_CH5, + MUX_9548_6_CH6, + MUX_9548_6_CH7, + MUX_9548_7_CH0, + MUX_9548_7_CH1, + MUX_9548_7_CH2, + MUX_9548_7_CH3, + MUX_9548_7_CH4, + MUX_9548_7_CH5, + MUX_9548_7_CH6, + MUX_9548_7_CH7, + MUX_9548_8_CH0, + MUX_9548_8_CH1, + MUX_9548_8_CH2, + MUX_9548_8_CH3, + MUX_9548_8_CH4, + MUX_9548_8_CH5, + MUX_9548_8_CH6, + MUX_9548_8_CH7, + MUX_9548_9_CH0, + MUX_9548_9_CH1, + MUX_9548_9_CH2, + MUX_9548_9_CH3, + MUX_9548_9_CH4, + MUX_9548_9_CH5, + MUX_9548_9_CH6, + MUX_9548_9_CH7, + MUX_9548_10_CH0, + MUX_9548_10_CH1, + MUX_9548_10_CH2, + MUX_9548_10_CH3, + MUX_9548_10_CH4, + MUX_9548_10_CH5, + MUX_9548_10_CH6, + MUX_9548_10_CH7, +}; + +#define I2C_ADDR_MUX_9555_0 (0x20) +#define I2C_ADDR_MUX_9555_1 (0x24) +#define I2C_ADDR_MUX_9555_2 (0x25) +#define I2C_ADDR_MUX_9555_3 (0x26) +#define I2C_ADDR_MUX_9539_0 (0x76) +#define I2C_ADDR_MUX_9539_1 (0x76) +#define I2C_BUS_FAN_STATUS (I2C_BUS_MAIN) +#define I2C_BUS_SYS_LED (MUX_9548_1_CH1) + +#define NUM_OF_I2C_MUX (11) +#define NUM_OF_CPLD (5) +#define NUM_OF_QSFP_PORT (64) +#define NUM_OF_SFP_PORT (2) +#define QSFP_EEPROM_I2C_ADDR (0x50) + +enum gpio_reg { + REG_PORT0_IN, + REG_PORT1_IN, + REG_PORT0_OUT, + REG_PORT1_OUT, + REG_PORT0_POL, + REG_PORT1_POL, + REG_PORT0_DIR, + REG_PORT1_DIR, +}; + +struct ing_i2c_board_info { + int ch; + int size; + struct i2c_board_info *board_info; +}; + +struct i2c_init_data { + __u16 ch; + __u16 addr; + __u8 reg; + __u8 value; +}; + +#endif diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/modules/ingrasys_s9130_32x_psu.c b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/modules/ingrasys_s9130_32x_psu.c new file mode 100644 index 000000000000..d3186a56b95c --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/modules/ingrasys_s9130_32x_psu.c @@ -0,0 +1,407 @@ +/* + * S9130-32x PSU driver + * + * Copyright (C) 2017 Ingrasys, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ingrasys_s9130_32x_platform.h" + +static ssize_t show_psu_eeprom(struct device *dev, + struct device_attribute *da, + char *buf); +static struct s9130_psu_data *s9130_psu_update_status(struct device *dev); +static struct s9130_psu_data *s9130_psu_update_eeprom(struct device *dev); +static int s9130_psu_read_block(struct i2c_client *client, + u8 command, + u8 *data, + int data_len); + + +#define DRIVER_NAME "psu" + +// Addresses scanned +static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; + +/* PSU EEPROM SIZE */ +#define EEPROM_SZ 256 +#define READ_EEPROM 1 +#define NREAD_EEPROM 0 + +static struct i2c_client pca9555_client; + +/* pca9555 gpio pin mapping */ +#define PSU2_PWROK 0 +#define PSU2_PRSNT_L 1 +#define PSU2_PWRON_L 2 +#define PSU1_PWROK 3 +#define PSU1_PRSNT_L 4 +#define PSU1_PWRON_L 5 +#define TMP_75_INT_L 6 + +/* Driver Private Data */ +struct s9130_psu_data { + struct device *hwmon_dev; + struct mutex lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + s32 status; /* IO expander value */ + char eeprom[EEPROM_SZ]; /* psu eeprom data */ + char psuABS; /* PSU absent */ + char psuPG; /* PSU power good */ +}; + +enum s9130_psu_sysfs_attributes { + PSU_POWER_GOOD, + PSU_ABSENT, + PSU_EEPROM +}; + +enum psu_index +{ + s9130_psu1, + s9130_psu2 +}; + +/* + * display power good attribute + */ +static ssize_t +show_psu_pg(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct s9130_psu_data *data = s9130_psu_update_status(dev); + unsigned int value; + + mutex_lock(&data->lock); + value = data->psuPG; + mutex_unlock(&data->lock); + + return sprintf(buf, "%d\n", value); +} + +/* + * display power absent attribute + */ +static ssize_t +show_psu_abs(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct s9130_psu_data *data = s9130_psu_update_status(dev); + unsigned int value; + + mutex_lock(&data->lock); + value = data->psuABS; + mutex_unlock(&data->lock); + + return sprintf(buf, "%d\n", value); +} + + +/* + * sysfs attributes for psu + */ +static SENSOR_DEVICE_ATTR(psu_pg, S_IRUGO, show_psu_pg, NULL, PSU_POWER_GOOD); +static SENSOR_DEVICE_ATTR(psu_abs, S_IRUGO, show_psu_abs, NULL, PSU_ABSENT); +static SENSOR_DEVICE_ATTR(psu_eeprom, S_IRUGO, show_psu_eeprom, NULL, PSU_EEPROM); + +static struct attribute *s9130_psu_attributes[] = { + &sensor_dev_attr_psu_pg.dev_attr.attr, + &sensor_dev_attr_psu_abs.dev_attr.attr, + &sensor_dev_attr_psu_eeprom.dev_attr.attr, + NULL +}; + +/* + * display psu eeprom content + */ +static ssize_t +show_psu_eeprom(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct s9130_psu_data *data = s9130_psu_update_eeprom(dev); + + memcpy(buf, (char *)data->eeprom, EEPROM_SZ); + return EEPROM_SZ; +} + +static const struct attribute_group s9130_psu_group = { + .attrs = s9130_psu_attributes, +}; + +/* + * check gpio expander is accessible + */ +static int +pca9555_detect(struct i2c_client *client) +{ + if (i2c_smbus_read_byte_data(client, REG_PORT0_DIR) < 0) { + return -ENODEV; + } + + return 0; +} + +/* + * client address init + */ +static void +i2c_devices_client_address_init(struct i2c_client *client) +{ + pca9555_client = *client; + pca9555_client.addr = 0x25; +} + +static int +s9130_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct s9130_psu_data *data; + int status, err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct s9130_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct s9130_psu_data)); + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->lock); + + i2c_devices_client_address_init(client); + + err = pca9555_detect(&pca9555_client); + if (err) { + return err; + } + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &s9130_psu_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &s9130_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int +s9130_psu_remove(struct i2c_client *client) +{ + struct s9130_psu_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &s9130_psu_group); + kfree(data); + + return 0; +} + + +/* + * psu eeprom read utility + */ +static int +s9130_psu_read_block(struct i2c_client *client, + u8 command, + u8 *data, + int data_len) +{ + int i=0, ret=0; + int blk_max = 32; //max block read size + + /* read eeprom, 32 * 8 = 256 bytes */ + for (i=0; i < EEPROM_SZ/blk_max; i++) { + ret = i2c_smbus_read_i2c_block_data(client, (i*blk_max), blk_max, + data + (i*blk_max)); + if (ret < 0) { + return ret; + } + } + return ret; +} + +/* + * update eeprom content + */ +static struct s9130_psu_data +*s9130_psu_update_eeprom(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s9130_psu_data *data = i2c_get_clientdata(client); + s32 status = 0; + int psu_pwrok = 0; + int psu_prsnt_l = 0; + + mutex_lock(&data->lock); + + if (time_after(jiffies, data->last_updated + 300 * HZ) + || !data->valid) { + + /* Read psu status */ + + status = i2c_smbus_read_word_data(&(pca9555_client), REG_PORT0_IN); + data->status = status; + + /*read psu status from io expander*/ + + if (data->index == s9130_psu1) { + psu_pwrok = PSU1_PWROK; + psu_prsnt_l = PSU1_PRSNT_L; + } else { + psu_pwrok = PSU2_PWROK; + psu_prsnt_l = PSU2_PRSNT_L; + } + data->psuPG = (status >> psu_pwrok) & 0x1; + data->psuABS = (status >> psu_prsnt_l) & 0x1; + + /* Read eeprom */ + if (!data->psuABS) { + //clear local eeprom data + memset(data->eeprom, 0, EEPROM_SZ); + + //read eeprom + status = s9130_psu_read_block(client, 0, data->eeprom, + ARRAY_SIZE(data->eeprom)); + + if (status < 0) { + memset(data->eeprom, 0, EEPROM_SZ); + dev_err(&client->dev, "Read eeprom failed, status=(%d)\n", status); + } else { + data->valid = 1; + } + } else { + memset(data->eeprom, 0, EEPROM_SZ); + } + data->last_updated = jiffies; + } + + mutex_unlock(&data->lock); + + return data; +} + +/* + * update psu status + */ +static struct s9130_psu_data +*s9130_psu_update_status(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s9130_psu_data *data = i2c_get_clientdata(client); + s32 status = 0; + int psu_pwrok = 0; + int psu_prsnt_l = 0; + + mutex_lock(&data->lock); + + /* Read psu status */ + + status = i2c_smbus_read_word_data(&(pca9555_client), REG_PORT0_IN); + data->status = status; + + /*read psu status from io expander*/ + + if (data->index == s9130_psu1) { + psu_pwrok = PSU1_PWROK; + psu_prsnt_l = PSU1_PRSNT_L; + } else { + psu_pwrok = PSU2_PWROK; + psu_prsnt_l = PSU2_PRSNT_L; + } + data->psuPG = (status >> psu_pwrok) & 0x1; + data->psuABS = (status >> psu_prsnt_l) & 0x1; + + mutex_unlock(&data->lock); + + return data; +} + +static const struct i2c_device_id s9130_psu_id[] = { + { "psu1", s9130_psu1 }, + { "psu2", s9130_psu2 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, s9130_psu_id); + +static struct i2c_driver s9130_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRIVER_NAME, + }, + .probe = s9130_psu_probe, + .remove = s9130_psu_remove, + .id_table = s9130_psu_id, + .address_list = normal_i2c, +}; + +static int __init s9130_psu_init(void) +{ + return i2c_add_driver(&s9130_psu_driver); +} + +static void __exit s9130_psu_exit(void) +{ + i2c_del_driver(&s9130_psu_driver); +} + +module_init(s9130_psu_init); +module_exit(s9130_psu_exit); + +MODULE_AUTHOR("Jason Tsai "); +MODULE_DESCRIPTION("S9130-32X psu driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/service/qsfp-monitor.service b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/service/qsfp-monitor.service new file mode 100644 index 000000000000..10b48f648b98 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/service/qsfp-monitor.service @@ -0,0 +1,15 @@ +[Unit] +Description= This QSFP Monitor service is to setup QSFP SI. +Requires=s9130-32x-monitor.service +After=s9130-32x-monitor.service + +[Service] +ExecStart=/usr/sbin/qsfp_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/service/s9130-32x-monitor.service b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/service/s9130-32x-monitor.service new file mode 100644 index 000000000000..3163b0af6f12 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/service/s9130-32x-monitor.service @@ -0,0 +1,19 @@ +[Unit] +Description= This Platform Monitor service is to initialize platform and monitor platform. +Before=platform-monitor.service +After=sysinit.target +Wants=fancontrol.service +Wants=qsfp-monitor.service +DefaultDependencies=no + +[Service] +ExecStartPre=/usr/sbin/i2c_utils.sh i2c_init +ExecStart=/usr/sbin/s9130_32x_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/utils/i2c_utils.sh b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/utils/i2c_utils.sh new file mode 100644 index 000000000000..71da18f6cab0 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/utils/i2c_utils.sh @@ -0,0 +1,1685 @@ +#!/bin/bash + +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +# trun on for more debug output +#DEBUG="on" + +VERSION="1.0.0" +TRUE=200 +FALSE=404 + +EXEC_FUNC=${1} +COLOR_LED=${2} +QSFP_PORT=${2} +QSFP_ACTION=${2} +MB_EEPROM_ACTION=${2} +ONOFF_LED=${3} +FAN_TRAY=${4} + +############################################################ +# Distributor ID: Debian +# Description: Debian GNU/Linux 8.6 (jessie) +# Release: 8.6 +# Codename: jessie +# Linux debian 3.16.0-4-amd64 #1 +# SMP Debian 3.16.36-1+deb8u1 (2016-09-03) x86_64 GNU/Linux +############################################################ + +# Color Definition +COLOR_TITLE="\e[1;32m" ### Green ### +COLOR_WARNING="\e[1;33m" ### Yellow ### +COLOR_ERROR="\e[1;31m" ### Red ### +COLOR_END="\e[0m" ### END ### + +NUM_I801_DEVICE=0 + +# MAIN MUX PCA9548#0 0x75 +NUM_MUX_9548_0_CH0=$(( ${NUM_I801_DEVICE} + 1 )) # ucd9090 0x34 +NUM_MUX_9548_0_CH1=$(( ${NUM_I801_DEVICE} + 2 )) # PCA9539 0x77 for fp LED & HW ID +NUM_MUX_9548_0_CH7=$(( ${NUM_I801_DEVICE} + 8 )) # W83795 0x2E + +# FRU MUX PCA9545#1 0x72 +NUM_MUX_9545_1_CH0=$(( ${NUM_I801_DEVICE} + 9 )) # PSU1 0x50 +NUM_MUX_9545_1_CH1=$(( ${NUM_I801_DEVICE} + 10 )) # PSU2 0x50 +NUM_MUX_9545_1_CH2=$(( ${NUM_I801_DEVICE} + 11 )) # FAN board IO exander 0x20 +NUM_MUX_9545_1_CH3=$(( ${NUM_I801_DEVICE} + 12 )) # TMP75#0 0x48 TMP75#1 0x49 + +# HOST MUX PCA9548#2 0X70 +NUM_MUX_9548_2_CH0=$(( ${NUM_I801_DEVICE} + 13 )) # PCA9548#3 0x71 +NUM_MUX_9548_2_CH1=$(( ${NUM_I801_DEVICE} + 14 )) # PCA9548#4 0x71 +NUM_MUX_9548_2_CH2=$(( ${NUM_I801_DEVICE} + 15 )) # PCA9548#5 0x71 +NUM_MUX_9548_2_CH3=$(( ${NUM_I801_DEVICE} + 16 )) # PCA9548#6 0x71 +NUM_MUX_9548_2_CH4=$(( ${NUM_I801_DEVICE} + 17 )) # PCA9535#3~6 0x20~0x23 ZQSFP ABS/INT +NUM_MUX_9548_2_CH5=$(( ${NUM_I801_DEVICE} + 18 )) # PCA9535#7~10 0x20~0x23 ZQSFP LPMODE/RST +NUM_MUX_9548_3_CH0=$(( ${NUM_I801_DEVICE} + 21 )) # QSFP 0 EEPROM +NUM_MUX_9548_4_CH0=$(( ${NUM_I801_DEVICE} + 29 )) # QSFP 8 EEPROM +NUM_MUX_9548_5_CH0=$(( ${NUM_I801_DEVICE} + 37 )) # QSFP 16 EEPROM +NUM_MUX_9548_6_CH0=$(( ${NUM_I801_DEVICE} + 45 )) # QSFP 24 EEPROM + +# MUX Alias +I2C_BUS_MAIN=${NUM_I801_DEVICE} +I2C_BUS_HWM=${NUM_MUX_9548_0_CH7} +I2C_BUS_FAN_STATUS=${NUM_MUX_9545_1_CH2} +I2C_BUS_SYS_LED=${NUM_MUX_9548_0_CH1} +I2C_BUS_HW_ID=${NUM_MUX_9548_0_CH1} +I2C_BUS_BMC_HW_ID=${I2C_BUS_MAIN} +I2C_BUS_PSU_STAT=${I2C_BUS_MAIN} +I2C_BUS_FANTRAY_LED=${NUM_MUX_9545_1_CH2} +I2C_BUS_MB_EEPROM=${I2C_BUS_MAIN} +I2C_BUS_CB_EEPROM=${I2C_BUS_MAIN} +I2C_BUS_PSU1_EEPROM=${NUM_MUX_9545_1_CH1} +I2C_BUS_PSU2_EEPROM=${NUM_MUX_9545_1_CH0} + +PATH_SYS_I2C_DEVICES="/sys/bus/i2c/devices" +PATH_HWMON_ROOT_DEVICES="/sys/class/hwmon" +PATH_HWMON_W83795_DEVICE="${PATH_HWMON_ROOT_DEVICES}/hwmon5" +PATH_I801_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_I801_DEVICE}" +PATH_MUX_9548_0_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CH0}" +PATH_MUX_9548_0_CH1="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CH1}" +PATH_MUX_9548_0_CH7="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CH7}" +PATH_MUX_9545_1_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9545_1_CH0}" +PATH_MUX_9545_1_CH1="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9545_1_CH1}" +PATH_MUX_9545_1_CH2="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9545_1_CH2}" +PATH_MUX_9545_1_CH3="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9545_1_CH3}" +PATH_MUX_9548_2_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH0}" +PATH_MUX_9548_2_CH1="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH1}" +PATH_MUX_9548_2_CH2="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH2}" +PATH_MUX_9548_2_CH3="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH3}" +PATH_MUX_9548_2_CH4="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH4}" +PATH_MUX_9548_2_CH5="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH5}" +PATH_MUX_9548_3_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_3_CH0}" +PATH_MUX_9548_4_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_4_CH0}" +PATH_MUX_9548_5_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_5_CH0}" +PATH_MUX_9548_6_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_6_CH0}" + +# I2C Address +### I2C MUX +I2C_ADDR_MUX_9548_0=0x73 # MAIN MUX +I2C_ADDR_MUX_9545_1=0x72 # FRU MUX +I2C_ADDR_MUX_9548_2=0x70 # HOST MUX +I2C_ADDR_MUX_9548_3=0x71 # ZQSFP MUX #1 EEPROM +I2C_ADDR_MUX_9548_4=0x71 # ZQSFP MUX #2 EEPROM +I2C_ADDR_MUX_9548_5=0x71 # ZQSFP MUX #3 EEPROM +I2C_ADDR_MUX_9548_6=0x71 # ZQSFP MUX #4 EEPROM +### GPIO Expander +I2C_ADDR_MUX_9539_0=0x76 # LED & HW ID +I2C_ADDR_MUX_9539_1=0x75 # BMC PRSNT & HWM reset +I2C_ADDR_MUX_9539_2=0x74 # SYS SEL & RST +I2C_ADDR_MUX_9535_3=0x20 # ZQSFP0~15 ABS +I2C_ADDR_MUX_9535_4=0x21 # ZQSFP16~31 ABS +I2C_ADDR_MUX_9535_5=0x22 # ZQSFP0~15 INT +I2C_ADDR_MUX_9535_6=0x23 # ZQSFP16~31 INT +I2C_ADDR_MUX_9535_7=0x20 # ZQSFP0~15 LPMODE +I2C_ADDR_MUX_9535_8=0x21 # ZQSFP16~31 LPMODE +I2C_ADDR_MUX_9535_9=0x22 # ZQSFP0~15 RST +I2C_ADDR_MUX_9535_10=0x23 # ZQSFP16~31 RST +I2C_ADDR_MUX_9535_11=0x20 # on FAN board, fan status and led config +I2C_ADDR_MUX_9555_12=0x24 # on BMC board, INT and HW ID +I2C_ADDR_MUX_9555_13=0x25 # on BMC board, PSU status +I2C_ADDR_MUX_9555_14=0x26 # on BMC board, RST and SEL +I2C_ADDR_MUX_9539_15=0x77 # on CPU board, STATUS and ERR from CPLD +### peripheral +I2C_ADDR_MB_EEPROM=0x55 # on main board +I2C_ADDR_CB_EEPROM=0x51 # on cpu board +I2C_ADDR_UCD9090=0x34 +I2C_ADDR_W83795=0x2F +I2C_ADDR_PSU1_EEPROM=0x50 +I2C_ADDR_PSU2_EEPROM=0x50 +I2C_ADDR_TMP75_REAR=0x4C +I2C_ADDR_TMP75_FRONT=0x49 +I2C_ADDR_TMP75_CB=0x4F # on cpu board +I2C_ADDR_TMP75_BB=0x4A # on bmc board +I2C_ADDR_QSFP_EEPROM=0x50 + +#sysfs +PATH_SYSFS_PSU1="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_PSU1_EEPROM}-$(printf "%04x" $I2C_ADDR_PSU1_EEPROM)" +PATH_SYSFS_PSU2="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_PSU2_EEPROM}-$(printf "%04x" $I2C_ADDR_PSU2_EEPROM)" + +#ACTIVE LOW enable flag +ACTIVE_LOW_EN=1 +ACTIVE_HIGH_EN=0 +#GPIO Direction In/Out +DIR_IN=in +DIR_OUT=out + +#Power Supply Status +PSU_DC_ON=1 +PSU_DC_OFF=0 +PSU_EXIST=1 +PSU_NOT_EXIST=0 + +# IO expander register +# direction +REG_PORT0_DIR=6 +REG_PORT1_DIR=7 +# polarity +REG_PORT0_POL=4 +REG_PORT1_POL=5 +# output +REG_PORT0_OUT=2 +REG_PORT1_OUT=3 +# input +REG_PORT0_IN=0 +REG_PORT1_IN=1 + +#ZQSFP GPIO sysfs index +ZQSFP_PORT0_ABS_GPIO_IDX=240 # 240~255 +ZQSFP_PORT16_ABS_GPIO_IDX=224 # 224~239 + +# switch port number range +MIN_PORT_NUM=1 +MAX_PORT_NUM=32 + +# Help usage function +function _help { + echo "=========================================================" + echo "# Description: Help Function" + echo "=========================================================" + echo "----------------------------------------------------" + echo "EX : ${0} help" + echo " : ${0} i2c_init" + echo " : ${0} i2c_deinit" + echo " : ${0} i2c_fan_speed_init" + echo " : ${0} i2c_io_exp_init" + echo " : ${0} i2c_gpio_init" + echo " : ${0} i2c_gpio_deinit" + echo " : ${0} i2c_led_test" + echo " : ${0} i2c_psu_eeprom_get" + echo " : ${0} i2c_mb_eeprom_get" + echo " : ${0} i2c_cb_eeprom_get" + echo " : ${0} i2c_eeprom_sync" + echo " : ${0} i2c_qsfp_eeprom_get [1-32]" + echo " : ${0} i2c_qsfp_eeprom_init new|delete" + echo " : ${0} i2c_mb_eeprom_init new|delete" + echo " : ${0} i2c_cb_eeprom_init new|delete" + echo " : ${0} i2c_qsfp_status_get [1-32]" + echo " : ${0} i2c_qsfp_type_get [1-32]" + echo " : ${0} i2c_board_type_get" + echo " : ${0} i2c_bmc_board_type_get" + echo " : ${0} i2c_psu_status" + echo " : ${0} i2c_led_psu_status_set" + echo " : ${0} i2c_led_fan_status_set" + echo " : ${0} i2c_led_fan_tray_status_set" + echo " : ${0} i2c_test_all" + echo " : ${0} i2c_sys_led green|amber" + echo " : ${0} i2c_fan_led green|amber on|off" + echo " : ${0} i2c_psu1_led green|amber on|off" + echo " : ${0} i2c_psu2_led green|amber on|off" + echo " : ${0} i2c_fan_tray_led green|amber on|off [1-4]" + echo "----------------------------------------------------" +} + +#Pause function +function _pause { + read -p "$*" +} + +#Retry command function +function _retry { + local i + for i in {1..5}; + do + eval "${*}" && break || echo "retry"; sleep 1; + done +} + +#I2C Init +function _i2c_init { + echo "=========================================================" + echo "# Description: I2C Init" + echo "=========================================================" + + #rmmod i2c_ismt + _util_rmmod i2c_i801 + modprobe i2c_i801 disable_features=0x10 + modprobe i2c_dev + modprobe i2c_mux_pca954x force_deselect_on_exit=1 + + # add MUX PCA9548#0 on I801, assume to be i2c-1~8 + if [ ! -e ${PATH_MUX_9548_0_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_0}' > ${PATH_I801_DEVICE}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_0} already init." + fi + # add MUX PCA9545#1 on I801, assume to be i2c-9~12 + if [ ! -e ${PATH_MUX_9545_1_CH0} ]; then + _retry "echo 'pca9545 ${I2C_ADDR_MUX_9545_1}' > ${PATH_I801_DEVICE}/new_device" + else + echo "pca9545 ${I2C_ADDR_MUX_9545_1} already init." + fi + # add MUX PCA9548#2 on I801, assume to be i2c-13~20 + if [ ! -e ${PATH_MUX_9548_2_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_2}' > ${PATH_I801_DEVICE}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_2} already init." + fi + # add MUX PCA9548#3 on PCA9548#2 CH0, assume to be i2c-21~28 + if [ ! -e ${PATH_MUX_9548_3_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_3}' > ${PATH_MUX_9548_2_CH0}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_3} already init." + fi + # add MUX PCA9548#4 on PCA9548#2 CH1, assume to be i2c-29~36 + if [ ! -e ${PATH_MUX_9548_4_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_4}' > ${PATH_MUX_9548_2_CH1}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_4} already init." + fi + # add MUX PCA9548#5 on PCA9548#2 CH2, assume to be i2c-37~44 + if [ ! -e ${PATH_MUX_9548_5_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_5}' > ${PATH_MUX_9548_2_CH2}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_5} already init." + fi + # add MUX PCA9548#6 on PCA9548#2 CH3, assume to be i2c-45~52 + if [ ! -e ${PATH_MUX_9548_6_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_6}' > ${PATH_MUX_9548_2_CH3}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_6} already init." + fi + + _i2c_hwm_init + _util_rmmod eeprom + modprobe eeprom_mb + modprobe gpio-pca953x + _i2c_io_exp_init + _i2c_gpio_init + _i2c_sensors_init + _i2c_psu_init + + # Init LED_CLR register (pull shift register out of reset), should be after io exp init + _port_led_clr_init + _i2c_qsfp_eeprom_init "new" + _i2c_mb_eeprom_init "new" + _i2c_cb_eeprom_init "new" + _i2c_fan_speed_init + _i2c_led_psu_status_set + _i2c_led_fan_status_set + + # sync eeprom content + _i2c_eeprom_sync + + # trun on sys led + echo "led_sys setup..." + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_sys_led + + _config_rmem +} + +#I2C Deinit +function _i2c_deinit { + echo "i2c deinit..." + _i2c_gpio_deinit + for mod in coretemp jc42 w83795 eeprom eeprom_mb gpio-pca953x i2c_mux_pca954x i2c_i801 ingrasys_s9130_32x_psu; + do + _util_rmmod $mod + done + echo "Done" +} + +function _i2c_sensors_init { + echo "SENSORS init..." + local dev_path + # to make sure hwmon index in sysfs as expected, + # need to remove kernel module and then probe them in expected order + # remove all sensors kernel module + _util_rmmod coretemp + _util_rmmod jc42 + _util_rmmod w83795 + # probe coretemp kernel module + modprobe coretemp + # probe hwmon kernel module + modprobe w83795 + # add tmp75 to sysfs + ####Main board thermal + dev_path="${PATH_SYS_I2C_DEVICES}/${NUM_MUX_9545_1_CH3}-$(printf "%04x" ${I2C_ADDR_TMP75_REAR})" + if ! [ -L ${dev_path} ]; then + echo "tmp75 ${I2C_ADDR_TMP75_REAR}" > ${PATH_MUX_9545_1_CH3}/new_device # hwmon1 + else + echo "${dev_path} already exist" + fi + dev_path="${PATH_SYS_I2C_DEVICES}/${NUM_MUX_9545_1_CH3}-$(printf "%04x" ${I2C_ADDR_TMP75_FRONT})" + if ! [ -L ${dev_path} ]; then + echo "tmp75 ${I2C_ADDR_TMP75_FRONT}" > ${PATH_MUX_9545_1_CH3}/new_device #hwmon2 + else + echo "${dev_path} already exist" + fi + ####BMC board thermal + dev_path="${PATH_SYS_I2C_DEVICES}/${NUM_MUX_9548_0_CH7}-$(printf "%04x" ${I2C_ADDR_TMP75_BB})" + if ! [ -L ${dev_path} ]; then + echo "tmp75 ${I2C_ADDR_TMP75_BB}" > ${PATH_MUX_9548_0_CH7}/new_device #hwmon3 + else + echo "${dev_path} already exist" + fi + ####CPU board thermal + dev_path="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MAIN}-$(printf "%04x" ${I2C_ADDR_TMP75_CB})" + if ! [ -L ${dev_path} ]; then + echo "tmp75 ${I2C_ADDR_TMP75_CB}" > ${PATH_I801_DEVICE}/new_device #hwmon4 + else + echo "${dev_path} already exist" + fi + # add w83795 to sysfs + dev_path="${PATH_SYS_I2C_DEVICES}/${NUM_MUX_9548_0_CH7}-$(printf "%04x" ${I2C_ADDR_W83795})" + if ! [ -L ${dev_path} ]; then + echo "w83795adg ${I2C_ADDR_W83795}" > ${PATH_MUX_9548_0_CH7}/new_device #hwmon5 + else + echo "${dev_path} already exist" + fi + + # probe jc42 kernel module + modprobe jc42 + + echo "Done" +} + +function _port_led_clr_init { + echo "port led init..." + # gpio pin on GPIO MUX PCA9539#2 I/O 0.2 + # pull high to out of reset + output_reg=${REG_PORT0_OUT} + mask=0x04 + value=0x04 + _util_i2cset -m ${mask} -y ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_2} ${output_reg} ${value} + echo "Done" +} + +#FAN Speed Init +function _i2c_fan_speed_init { + echo -n "FAN SPEED INIT..." + if [ -e "${PATH_HWMON_W83795_DEVICE}" ]; then + # init fan speed + echo 120 > ${PATH_HWMON_W83795_DEVICE}/device/pwm1 + echo 120 > ${PATH_HWMON_W83795_DEVICE}/device/pwm2 + echo "SUCCESS" + else + echo "FAIL" + fi +} + +# HWM init +function _i2c_hwm_init { + echo "HWM INIT..." + # select bank0 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x00 0x80 + # SW reset, Disable monitor + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x01 0x9C + # disable TR5/TR6 DTS + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x04 0x0 + # enable FANIN1~8 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x06 0xFF + # disable FANIN9~14 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x07 0x00 + # CLKIN clock frequency set as 48Mhz + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x01 0x1C + # select bank 2 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x00 0x82 + # set PWM mode in FOMC + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x0F 0x00 + # set 25KHz fan output frequency in F1OPFP&F2OPFP + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x18 0x84 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x19 0x84 +} + +#Temperature sensor Init +function _i2c_temp_init { + echo "TEMP INIT..." + # enable temp monitor on w83795 + # select bank0 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x00 0x80 + # enable TR4 temperature monitoring + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x05 0x40 + # disable TR5/TR6 DTS + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x04 0x0 + echo "Done" +} + +#VOLMON Init +function _i2c_volmon_init { + echo "VOLMON INIT..." + # enable voltage monitor on w83795 + # VSEN1 P0V9 + # VSEN2 VDD + # VSEN3 P1V2 + # VSEN4 P1V8 + # select bank0 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x00 0x80 + # enable vsen1~4, disable vsen5~8 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x02 0x0F + # enable 3VDD,VBAT + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x03 0x50 + echo "Done" +} + +#FANIN Init +function _i2c_fan_init { + echo "FANIN INIT..." + # enable fan monitor on w83795 + # 4 fantray with 8 FANIN + # select bank0 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x00 0x80 + # enable FANIN1~8 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x06 0xFF + # disable FANIN9~14 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x07 0x00 + + # select bank 2 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x00 0x82 + # set PWM mode in FOMC + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x0F 0x00 + + echo "Done" +} + +#IO Expander Init +function _i2c_io_exp_init { + echo "=========================================================" + echo "# Description: I2C IO Expender Init" + echo "=========================================================" + + # need to init BMC io expander first due to some io expander are reset default + echo "Init BMC INT & HW ID IO Expander" + # all input + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_12} ${REG_PORT0_DIR} 0xFF + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_12} ${REG_PORT1_DIR} 0xFF + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_12} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_12} ${REG_PORT1_POL} 0x00 + + echo "Init BMC PSU status IO Expander" + # PWRON default 0 (ACTIVE_LOW) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_13} ${REG_PORT0_OUT} 0x00 + # default 0 (ACTIVE_LOW) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_13} ${REG_PORT1_OUT} 0x00 + # I/O 0.2 0.5 output(PWRON), rest input + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_13} ${REG_PORT0_DIR} 0xDB + # I/O 1.0~1.1 input, 1.2~1.4 output (1.5~1.7 not enable) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_13} ${REG_PORT1_DIR} 0xE3 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_13} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_13} ${REG_PORT1_POL} 0x00 + + echo "Init BMC RST and SEL IO Expander" + # RST default is 1 (ACTIVE_LOW) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_14} ${REG_PORT0_OUT} 0x3F + # SEL default is 0 (HOST), EN default is 1 (ACTIVE_HIGH) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_14} ${REG_PORT1_OUT} 0x1F + # I/O 0.0~0.5 output, 0.6~0.7 not use + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_14} ${REG_PORT0_DIR} 0xC0 + # all output + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_14} ${REG_PORT1_DIR} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_14} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_14} ${REG_PORT1_POL} 0x00 + + echo "Init System LED & HW ID IO Expander" + # I/O_0.x for System LED default 0, I/O_1.x for HW ID + _util_i2cset -y -r ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${REG_PORT0_OUT} 0x00 + # System LED => all output + _util_i2cset -y -r ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${REG_PORT0_DIR} 0x00 + # HW ID => all input + _util_i2cset -y -r ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${REG_PORT1_DIR} 0xFF + _util_i2cset -y -r ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${REG_PORT1_POL} 0x00 + + echo "Init System PRSNT and HWM RST IO Expander" + # HWM_RST_L default 1 (ACTIVE_LOW) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_1} ${REG_PORT0_OUT} 0x04 + # all input expect HWM_RST_L (0.2) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_1} ${REG_PORT0_DIR} 0xFB + # port1 not used + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_1} ${REG_PORT1_DIR} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_1} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_1} ${REG_PORT1_POL} 0x00 + + echo "Init System SEL and RST IO Expander" + # RST 0.0~0.3 default 1 (ACTIVE low), rest default 0 + # SEL set to value 0 (host) + # LED_CLR also do init in _port_led_clr_init + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_2} ${REG_PORT0_OUT} 0x0F + # RST 1.6~1.7 default 1 (ACTIVE low), INT 1.0~1.4 default 1 (ACTIVE low) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_2} ${REG_PORT1_OUT} 0xDF + # all output, but MAC_RST_L 0.0 need to set as input to prevent reboot issue + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_2} ${REG_PORT0_DIR} 0x09 + # RST 1.5 !~ 1.7 output, rest are input + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_2} ${REG_PORT1_DIR} 0x1F + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_2} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_2} ${REG_PORT1_POL} 0x00 + + echo "Init FAN Board Status IO Expander" + # LED_G_L set to 0, LED_Y_L set to 1 (ACTIVE_LOW) + _util_i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_11} ${REG_PORT0_OUT} 0x22 + _util_i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_11} ${REG_PORT1_OUT} 0x22 + # DIR/ABS is input, LED_Y/LED_G is output + _util_i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_11} ${REG_PORT0_DIR} 0xCC + _util_i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_11} ${REG_PORT1_DIR} 0xCC + _util_i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_11} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9535_11} ${REG_PORT1_POL} 0x00 + + echo "Init CPU CPLD IO Expander" + # all input + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_15} ${REG_PORT0_DIR} 0xFF + # all input + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_15} ${REG_PORT1_DIR} 0xFF + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_15} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_15} ${REG_PORT1_POL} 0x00 + + echo "Init ZQSFP IO Expender" + + echo "set ZQSFP ABS" + #zQSFP 0-15 ABS + # all input + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_3} ${REG_PORT0_DIR} 0xFF + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_3} ${REG_PORT1_DIR} 0xFF + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_3} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_3} ${REG_PORT1_POL} 0x00 + #zQSFP 16-31 ABS + # all input + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_4} ${REG_PORT0_DIR} 0xFF + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_4} ${REG_PORT1_DIR} 0xFF + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_4} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_4} ${REG_PORT1_POL} 0x00 + + echo "set ZQSFP INT" + #zQSFP 0-15 INT + # all input + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_5} ${REG_PORT0_DIR} 0xFF + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_5} ${REG_PORT1_DIR} 0xFF + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_5} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_5} ${REG_PORT1_POL} 0x00 + #zQSFP 16-31 INT + # all input + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_6} ${REG_PORT0_DIR} 0xFF + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_6} ${REG_PORT1_DIR} 0xFF + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_6} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH4} ${I2C_ADDR_MUX_9535_6} ${REG_PORT1_POL} 0x00 + + echo "set ZQSFP LP_MODE = 0" + #ZQSFP 0-15 LP_MODE + # default is 0 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_7} ${REG_PORT0_OUT} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_7} ${REG_PORT1_OUT} 0x00 + # all output + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_7} ${REG_PORT0_DIR} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_7} ${REG_PORT1_DIR} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_7} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_7} ${REG_PORT1_POL} 0x00 + #ZQSFP 16-31 LP_MODE + # default is 0 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_8} ${REG_PORT0_OUT} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_8} ${REG_PORT1_OUT} 0x00 + # all output + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_8} ${REG_PORT0_DIR} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_8} ${REG_PORT1_DIR} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_8} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_8} ${REG_PORT1_POL} 0x00 + + echo "set ZQSFP RST = 1" + #ZQSFP 0-15 RST + # default is 1 (ACTIVE_LOW) + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_9} ${REG_PORT0_OUT} 0xFF + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_9} ${REG_PORT1_OUT} 0xFF + # all output + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_9} ${REG_PORT0_DIR} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_9} ${REG_PORT1_DIR} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_9} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_9} ${REG_PORT1_POL} 0x00 + #ZQSFP 16-31 RST + # default is 1 (ACTIVE_LOW) + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_10} ${REG_PORT0_OUT} 0xFF + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_10} ${REG_PORT1_OUT} 0xFF + # all output + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_10} ${REG_PORT0_DIR} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_10} ${REG_PORT1_DIR} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_10} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_2_CH5} ${I2C_ADDR_MUX_9535_10} ${REG_PORT1_POL} 0x00 + +} + +#GPIO Init +function _i2c_gpio_init { + local i=0 + local start=255 + local end=255 + local ch_num=16 + + #ABS Port 0-15 + echo "pca9535 ${I2C_ADDR_MUX_9535_3}" > ${PATH_MUX_9548_2_CH4}/new_device + start=$[ ${end}-${ch_num}+1] + for (( i=$start; i<=$end; i++ )) + do + _util_gpio_export ${i} ${DIR_IN} ${ACTIVE_LOW_EN} + done + + #ABS Port 16-31 + end=$[ ${start}-1] + echo "pca9535 ${I2C_ADDR_MUX_9535_4}" > ${PATH_MUX_9548_2_CH4}/new_device + start=$[ ${end}-${ch_num}+1] + for (( i=$start; i<=$end; i++ )) + do + _util_gpio_export ${i} ${DIR_IN} ${ACTIVE_LOW_EN} + done + + #INT Port 0-15 + end=$[ ${start}-1] + echo "pca9535 ${I2C_ADDR_MUX_9535_5}" > ${PATH_MUX_9548_2_CH4}/new_device + start=$[ ${end}-${ch_num}+1] + for (( i=$start; i<=$end; i++ )) + do + _util_gpio_export ${i} ${DIR_IN} ${ACTIVE_LOW_EN} + done + + #INT Port 16-31 + end=$[ ${start}-1] + echo "pca9535 ${I2C_ADDR_MUX_9535_6}" > ${PATH_MUX_9548_2_CH4}/new_device + start=$[ ${end}-${ch_num}+1] + for (( i=$start; i<=$end; i++ )) + do + _util_gpio_export ${i} ${DIR_IN} ${ACTIVE_LOW_EN} + done + + #LP Mode Port 0-15 + end=$[ ${start}-1] + echo "pca9535 ${I2C_ADDR_MUX_9535_7}" > ${PATH_MUX_9548_2_CH5}/new_device + start=$[ ${end}-${ch_num}+1] + for (( i=$start; i<=$end; i++ )) + do + _util_gpio_export ${i} ${DIR_OUT} ${ACTIVE_HIGH_EN} + done + + #LP Mode Port 16-31 + end=$[ ${start}-1] + echo "pca9535 ${I2C_ADDR_MUX_9535_8}" > ${PATH_MUX_9548_2_CH5}/new_device + start=$[ ${end}-${ch_num}+1] + for (( i=$start; i<=$end; i++ )) + do + _util_gpio_export ${i} ${DIR_OUT} ${ACTIVE_HIGH_EN} + done + + #RESET Port 0-15 + end=$[ ${start}-1] + echo "pca9535 ${I2C_ADDR_MUX_9535_9}" > ${PATH_MUX_9548_2_CH5}/new_device + start=$[ ${end}-${ch_num}+1] + for (( i=$start; i<=$end; i++ )) + do + # need to set value to low (became ACTIVE_HIGH) to take port out of reset + _util_gpio_export ${i} ${DIR_OUT} ${ACTIVE_LOW_EN} 0 + done + + #RESET Port 16-31 + end=$[ ${start}-1] + echo "pca9535 ${I2C_ADDR_MUX_9535_10}" > ${PATH_MUX_9548_2_CH5}/new_device + start=$[ ${end}-${ch_num}+1] + for (( i=$start; i<=$end; i++ )) + do + # need to set value to low (became ACTIVE_HIGH) to take port out of reset + _util_gpio_export ${i} ${DIR_OUT} ${ACTIVE_LOW_EN} 0 + done +} + +#GPIO DeInit +function _i2c_gpio_deinit { + echo ${I2C_ADDR_MUX_9535_3} > /sys/bus/i2c/devices/i2c-${NUM_MUX_9548_2_CH4}/delete_device + echo ${I2C_ADDR_MUX_9535_4} > /sys/bus/i2c/devices/i2c-${NUM_MUX_9548_2_CH4}/delete_device + echo ${I2C_ADDR_MUX_9535_5} > /sys/bus/i2c/devices/i2c-${NUM_MUX_9548_2_CH4}/delete_device + echo ${I2C_ADDR_MUX_9535_6} > /sys/bus/i2c/devices/i2c-${NUM_MUX_9548_2_CH4}/delete_device + echo ${I2C_ADDR_MUX_9535_7} > /sys/bus/i2c/devices/i2c-${NUM_MUX_9548_2_CH5}/delete_device + echo ${I2C_ADDR_MUX_9535_8} > /sys/bus/i2c/devices/i2c-${NUM_MUX_9548_2_CH5}/delete_device + echo ${I2C_ADDR_MUX_9535_9} > /sys/bus/i2c/devices/i2c-${NUM_MUX_9548_2_CH5}/delete_device + echo ${I2C_ADDR_MUX_9535_10} > /sys/bus/i2c/devices/i2c-${NUM_MUX_9548_2_CH5}/delete_device +} + +#Set FAN Tray LED +function _i2c_led_fan_tray_status_set { + echo "FAN Tray Status Setup" + #FAN Status get + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan8_alarm` + FAN2_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan6_alarm` + FAN4_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan4_alarm` + FAN6_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan2_alarm` + FAN8_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + + # check if io expander for fan tray exist + result=`i2cget -y ${I2C_BUS_FANTRAY_LED} ${I2C_ADDR_MUX_9535_11} ${REG_PORT0_IN} 2>/dev/null` + err_code=$? + if [ "$err_code" != "0" ]; then + echo "fan tray not exist!" + return + fi + + if [ "${FAN1_ALARM}" == "0" ] && [ "${FAN2_ALARM}" == "0" ]; then + FAN_TRAY=1 + echo "FAN_TRAY${FAN_TRAY}..." + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=1 + echo "FAN_TRAY${FAN_TRAY}..." + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + + if [ "${FAN3_ALARM}" == "0" ] && [ "${FAN4_ALARM}" == "0" ]; then + FAN_TRAY=2 + echo "FAN_TRAY${FAN_TRAY}..." + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=2 + echo "FAN_TRAY${FAN_TRAY}..." + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + + if [ "${FAN5_ALARM}" == "0" ] && [ "${FAN6_ALARM}" == "0" ]; then + FAN_TRAY=3 + echo "FAN_TRAY${FAN_TRAY}..." + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=3 + echo "FAN_TRAY${FAN_TRAY}..." + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi + + if [ "${FAN7_ALARM}" == "0" ] && [ "${FAN8_ALARM}" == "0" ]; then + FAN_TRAY=4 + echo "FAN_TRAY${FAN_TRAY}..." + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + else + FAN_TRAY=4 + echo "FAN_TRAY${FAN_TRAY}..." + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_tray_led + fi +} + +#Set FAN LED +function _i2c_led_fan_status_set { + echo "FAN Status Setup" + #PSU Status set + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN2_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan2_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN4_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan4_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN6_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan6_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + FAN8_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan8_alarm` + + echo "led_fan setup..." + # all fan ok + if [ "${FAN1_ALARM}" == "0" ] && [ "${FAN2_ALARM}" == "0" ] \ + && [ "${FAN3_ALARM}" == "0" ] && [ "${FAN4_ALARM}" == "0" ] \ + && [ "${FAN5_ALARM}" == "0" ] && [ "${FAN6_ALARM}" == "0" ] \ + && [ "${FAN7_ALARM}" == "0" ] && [ "${FAN8_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + # all fan fail + elif [ "${FAN1_ALARM}" == "1" ] && [ "${FAN2_ALARM}" == "1" ] \ + && [ "${FAN3_ALARM}" == "1" ] && [ "${FAN4_ALARM}" == "1" ] \ + && [ "${FAN5_ALARM}" == "1" ] && [ "${FAN6_ALARM}" == "1" ] \ + && [ "${FAN7_ALARM}" == "1" ] && [ "${FAN8_ALARM}" == "1" ]; then + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + # partial fan fail + else + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_fan_led + fi +} + +#Set Power Supply LED +function _i2c_led_psu_status_set { + echo "PSU LED Status Setup" + + #PSU Status set + _i2c_psu_status + + #PSU1 Status + echo "led_psu1 setup..." + if [ "${psu1Exist}" == ${PSU_EXIST} ]; then + if [ "${psu1PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + else + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + fi + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu1_led + fi + + #PSU2 Status + echo "led_psu2 setup..." + if [ "${psu2Exist}" == ${PSU_EXIST} ]; then + if [ "${psu2PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + else + COLOR_LED="amber" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + fi + else + COLOR_LED="green" + ONOFF_LED="off" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_psu2_led + fi +} + +#LED Test +function _i2c_led_test { + echo "=========================================================" + echo "# Description: I2C SYSTEM LED TEST..." + echo "=========================================================" + local output_reg=${REG_PORT0_OUT} + local mask=0xFF + local value=0xFF + + #sys led (green) + # set sys_led_g (0.7) = 1 + mask=0x80 + value=0x80 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check SYS LED green light and Press [Enter] key to continue...' + #sys led (amber) + # set sys_led_g (0.7) = 0 + mask=0x80 + value=0x00 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check SYS LED amber light and Press [Enter] key to continue...' + + #FAN led (green) + # set fan_led_en (0.6) = 1 & fan_led_y (0.5) = 0 + mask=0x60 + value=0x40 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check FAN LED green light and Press [Enter] key to continue...' + #FAN led (amber) + # set fan_led_en (0.6) = 1 & fan_led_y (0.5) = 1 + mask=0x60 + value=0x60 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check FAN LED amber light and Press [Enter] key to continue...' + + #PSU1 led (green) + # set psu1_pwr_ok_oe (0.4) = 1 & psu1_led_y (0.3) = 0 + mask=0x18 + value=0x10 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check PSU1 LED green light and Press [Enter] key to continue...' + #PSU1 led (amber) + # set psu1_pwr_ok_oe (0.4) = 1 & psu1_led_y (0.3) = 1 + mask=0x18 + value=0x18 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check PSU1 LED amber light and Press [Enter] key to continue...' + + #PSU2 led (green) + # set psu0_pwr_ok_oe (0.2) = 1 & psu0_led_y (0.1) = 0 + mask=0x06 + value=0x04 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check PSU2 LED green light and Press [Enter] key to continue...' + #PSU2 led (amber) + # set psu0_pwr_ok_oe (0.2) = 1 & psu0_led_y (0.1) = 1 + mask=0x06 + value=0x06 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check PSU2 LED amber light and Press [Enter] key to continue...' + + #Turn OFF All LED (can't trun off system led) + # set set fan_led_en (0.6), psu1_pwr_ok_oe (0.4), psu0_pwr_ok_oe (0.2) = 0 + mask=0x54 + value=0x00 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check turn off all LEDs (exclude SYS LED) and Press [Enter] key to continue...' + + # restore sys led + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_sys_led + + echo "Done" +} + +#Set QSFP Port variable +function _qsfp_port_i2c_var_set { + local port=$1 + case ${port} in + 1|2|3|4|5|6|7|8) + i2cbus=${NUM_MUX_9548_2_CH4} + eeprombusbase=${NUM_MUX_9548_3_CH0} + gpioBase=${ZQSFP_PORT0_ABS_GPIO_IDX} + ;; + 9|10|11|12|13|14|15|16) + i2cbus=${NUM_MUX_9548_2_CH4} + eeprombusbase=${NUM_MUX_9548_4_CH0} + gpioBase=${ZQSFP_PORT0_ABS_GPIO_IDX} + ;; + 17|18|19|20|21|22|23|24) + i2cbus=${NUM_MUX_9548_2_CH4} + eeprombusbase=${NUM_MUX_9548_5_CH0} + gpioBase=${ZQSFP_PORT16_ABS_GPIO_IDX} + ;; + 25|26|27|28|29|30|31|32) + i2cbus=${NUM_MUX_9548_2_CH4} + eeprombusbase=${NUM_MUX_9548_6_CH0} + gpioBase=${ZQSFP_PORT16_ABS_GPIO_IDX} + ;; + *) + echo "Please input 1~32" + ;; + esac +} + +#Set QSFP Port variable +function _qsfp_eeprom_var_set { + local port=$1 + # port 1 => zqsfp0 + # port 2 => zqsfp1 + # ... + eeprombusidx=$(( (${port} - 1) % 8)) + eeprombus=$(( ${eeprombusbase} + ${eeprombusidx} )) + eepromAddr=${I2C_ADDR_QSFP_EEPROM} +} + +#Get QSFP EEPROM Information +function _i2c_qsfp_eeprom_get { + + # input parameter validation + _util_input_check ${QSFP_PORT} ${MIN_PORT_NUM} ${MAX_PORT_NUM} + + _util_get_qsfp_abs + + if [ $status = 0 ]; then + exit + fi + + _qsfp_eeprom_var_set ${QSFP_PORT} + + cat ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/eeprom | hexdump -C +} + +#Init QSFP EEPROM +function _i2c_qsfp_eeprom_init { + echo "QSFP EEPROM INIT..." + + #Action check + action=$1 + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init 1-32 ports EEPROM + local i + for i in {1..32}; + do + _qsfp_port_i2c_var_set ${i} + + _qsfp_eeprom_var_set ${i} + + if [ "${action}" == "new" ] && \ + ! [ -L ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr) ]; then + echo "sff8436 $eepromAddr" > ${PATH_SYS_I2C_DEVICES}/i2c-$eeprombus/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr) ]; then + echo "$eepromAddr" > ${PATH_SYS_I2C_DEVICES}/i2c-$eeprombus/delete_device + fi + done + echo "Done" +} + +#Init Main Board EEPROM +function _i2c_mb_eeprom_init { + echo -n "Main Board EEPROM INIT..." + + #Action check + action=$1 + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init mb EEPROM + if [ "${action}" == "new" ] && \ + ! [ -L ${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MB_EEPROM}-$(printf "%04x" $I2C_ADDR_MB_EEPROM) ]; then + echo "mb_eeprom ${I2C_ADDR_MB_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_MB_EEPROM}/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MB_EEPROM}-$(printf "%04x" $I2C_ADDR_MB_EEPROM) ]; then + echo "$I2C_ADDR_MB_EEPROM" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_MB_EEPROM}/delete_device + fi + echo "Done" +} + +#Init CPU Board EEPROM +function _i2c_cb_eeprom_init { + echo -n "CPU Board EEPROM INIT..." + + #Action check + action=$1 + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init cpu EEPROM + if [ "${action}" == "new" ] && \ + ! [ -L ${PATH_SYS_I2C_DEVICES}/${I2C_BUS_CB_EEPROM}-$(printf "%04x" $I2C_ADDR_CB_EEPROM) ]; then + echo "mb_eeprom ${I2C_ADDR_CB_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_CB_EEPROM}/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${PATH_SYS_I2C_DEVICES}/${I2C_BUS_CB_EEPROM}-$(printf "%04x" $I2C_ADDR_CB_EEPROM) ]; then + echo "$I2C_ADDR_CB_EEPROM" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_CB_EEPROM}/delete_device + fi + echo "Done" +} + + +#get QSFP Status +function _i2c_qsfp_status_get { + + # input parameter validation + _util_input_check ${QSFP_PORT} ${MIN_PORT_NUM} ${MAX_PORT_NUM} + + local stat + _util_get_qsfp_abs + echo "status=$status" +} + +#get QSFP Type +function _i2c_qsfp_type_get { + + # input parameter validation + _util_input_check ${QSFP_PORT} ${MIN_PORT_NUM} ${MAX_PORT_NUM} + + _qsfp_port_i2c_var_set ${QSFP_PORT} + + _qsfp_eeprom_var_set ${QSFP_PORT} + + #Get QSFP EEPROM info + qsfp_info=$(base64 ${PATH_SYS_I2C_DEVICES}/$eeprombus-$(printf "%04x" $eepromAddr)/eeprom) + + identifier=$(echo $qsfp_info | base64 -d -i | hexdump -s 128 -n 1 -e '"%x"') + connector=$(echo $qsfp_info | base64 -d -i | hexdump -s 130 -n 1 -e '"%x"') + transceiver=$(echo $qsfp_info | base64 -d -i | hexdump -s 131 -n 1 -e '"%x"') + + echo "identifier=$identifier" + echo "connector=$connector" + echo "transceiver=$transceiver" +} + +#Init PSU Kernel Module +function _i2c_psu_init { + echo "=========================================================" + echo "# Description: I2C PSU Init" + echo "=========================================================" + modprobe ingrasys_s9130_32x_psu + + echo "psu1 ${I2C_ADDR_PSU1_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_PSU1_EEPROM}/new_device + echo "psu2 ${I2C_ADDR_PSU2_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_PSU2_EEPROM}/new_device +} + +#Get PSU EEPROM Information +function _i2c_psu_eeprom_get { + local eeprom_psu1="" + local eeprom_psu2="" + + echo "=========================================================" + echo "# Description: I2C PSU EEPROM Get..." + echo "=========================================================" + + eeprom_psu1="${PATH_SYSFS_PSU1}/psu_eeprom" + cat ${eeprom_psu1} | hexdump -C + + eeprom_psu2="${PATH_SYSFS_PSU2}/psu_eeprom" + cat ${eeprom_psu2} | hexdump -C +} + +#Get Main Board EEPROM Information +function _i2c_mb_eeprom_get { + echo "=========================================================" + echo "# Description: I2C MB EEPROM Get..." + echo "=========================================================" + _i2c_sys_eeprom_get mb +} + +#Get CPU Board EEPROM Information +function _i2c_cb_eeprom_get { + echo "=========================================================" + echo "# Description: I2C CB EEPROM Get..." + echo "=========================================================" + _i2c_sys_eeprom_get cb +} + +#Get system EEPROM Information +##input: "cb" for cpu board, "mb" for main board +function _i2c_sys_eeprom_get { + local eeprom_dev + + if [ "$1" == "cb" ]; then + eeprom_dev="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_CB_EEPROM}-$(printf "%04x" $I2C_ADDR_CB_EEPROM)/eeprom" + elif [ "$1" == "mb" ]; then + eeprom_dev="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MB_EEPROM}-$(printf "%04x" $I2C_ADDR_MB_EEPROM)/eeprom" + else + echo "wrong eeprom type" + return + fi + + # check if eeprom device exist in sysfs + if [ ! -f ${eeprom_dev} ]; then + echo "eeprom device not init" + return + fi + + cat ${eeprom_dev} | hexdump -C + echo "Done" +} + +#sync eeprom content between mb and cb eeprom +function _i2c_eeprom_sync { + echo "=========================================================" + echo "# Description: EEPROM sync..." + echo "=========================================================" + + local mb_eeprom_dev="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MB_EEPROM}-$(printf "%04x" $I2C_ADDR_MB_EEPROM)/eeprom" + local cb_eeprom_dev="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_CB_EEPROM}-$(printf "%04x" $I2C_ADDR_CB_EEPROM)/eeprom" + + # check if eeprom device exist in sysfs + if [[ ! -f ${mb_eeprom_dev} || ! -f ${cb_eeprom_dev} ]]; then + echo "eeprom device not init" + return + fi + + ## check if MB eeprom is empty + if [ ! -z "$(cat ${mb_eeprom_dev} | hexdump -n2 | grep ffff)" ]; then + echo "copy cb eeprom to mb eeprom..." + cat ${cb_eeprom_dev} > ${mb_eeprom_dev} + else + echo "no need to sync" + fi + + echo "Done" +} + +#Set System Status LED +function _i2c_sys_led { + # only green/amber, on/off can't control + if [ "${COLOR_LED}" == "green" ]; then + # set sys_led_g (0.7) = 1 + output_reg=${REG_PORT0_OUT} + mask=0x80 + value=0x80 + elif [ "${COLOR_LED}" == "amber" ]; then + # set sys_led_g (0.7) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x80 + value=0x00 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + #apply to io expander + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + echo "Done" +} + +#Set FAN LED +function _i2c_fan_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + # set fan_led_en (0.6) = 1 & fan_led_y (0.5) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x60 + value=0x40 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + # set fan_led_en (0.6) = 0 & fan_led_y (0.5) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x60 + value=0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + # set fan_led_en (0.6) = 1 & fan_led_y (0.5) = 1 + output_reg=${REG_PORT0_OUT} + mask=0x60 + value=0x60 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + # set fan_led_en (0.6) = 0 & fan_led_y (0.5) = 1 + output_reg=${REG_PORT0_OUT} + mask=0x60 + value=0x20 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + #apply to io expander + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + echo "Done" +} + +#Set PSU1 LED +function _i2c_psu1_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + # set psu1_pwr_ok_oe (0.4) = 1 & psu1_led_y (0.3) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x18 + value=0x10 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + # set psu1_pwr_ok_oe (0.4) = 0 & psu1_led_y (0.3) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x18 + value=0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + # set psu1_pwr_ok_oe (0.4) = 1 & psu1_led_y (0.3) = 1 + output_reg=${REG_PORT0_OUT} + mask=0x18 + value=0x18 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + # set psu1_pwr_ok_oe (0.4) = 0 & psu1_led_y (0.3) = 1 + output_reg=${REG_PORT0_OUT} + mask=0x18 + value=0x08 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + #apply to io expander + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + echo "Done" +} + +#Set PSU2 LED +function _i2c_psu2_led { + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + # set psu0_pwr_ok_oe (0.2) = 1 & psu0_led_y (0.1) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x06 + value=0x04 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + # set psu0_pwr_ok_oe (0.2) = 0 & psu0_led_y (0.1) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x06 + value=0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + # set psu0_pwr_ok_oe (0.2) = 1 & psu0_led_y (0.1) = 1 + output_reg=${REG_PORT0_OUT} + mask=0x06 + value=0x06 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + # set psu0_pwr_ok_oe (0.2) = 0 & psu0_led_y (0.1) = 1 + output_reg=${REG_PORT0_OUT} + mask=0x06 + value=0x02 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + #apply to io expander + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + echo "Done" +} + +#Set FAN Tray LED +function _i2c_fan_tray_led { + + i2cAddr=${I2C_ADDR_MUX_9535_11} + output_reg=${REG_PORT0_OUT} + + case ${FAN_TRAY} in + 4) + output_reg=${REG_PORT0_OUT} + if [ "${COLOR_LED}" == "green" ]; then + mask=0x01 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x02 + fi + ;; + 3) + output_reg=${REG_PORT0_OUT} + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + 2) + output_reg=${REG_PORT1_OUT} + if [ "${COLOR_LED}" == "green" ]; then + mask=0x01 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x02 + fi + ;; + 1) + output_reg=${REG_PORT1_OUT} + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + *) + echo "Please input 1~4" + exit + ;; + esac + + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + _util_i2cset -m $mask -y -r ${I2C_BUS_FANTRAY_LED} $i2cAddr ${output_reg} 0x00 + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + _util_i2cset -m $mask -y -r ${I2C_BUS_FANTRAY_LED} $i2cAddr ${output_reg} 0x33 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + _util_i2cset -m $mask -y -r ${I2C_BUS_FANTRAY_LED} $i2cAddr ${output_reg} 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + _util_i2cset -m $mask -y -r ${I2C_BUS_FANTRAY_LED} $i2cAddr ${output_reg} 0x33 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "Done" +} + +#Get Board Version and Type +function _i2c_board_type_get { + # read input port 1 value from io expander + input_reg=${REG_PORT1_IN} + boardType=`i2cget -y ${I2C_BUS_HW_ID} ${I2C_ADDR_MUX_9539_0} ${input_reg}` + boardBuildRev=$((($boardType) & 0x03)) + boardHwRev=$((($boardType) >> 2 & 0x03)) + boardId=$((($boardType) >> 4)) + printf "MAIN_BOARD BOARD_ID is 0x%02x, HW Rev %d, Build Rev %d\n" $boardId $boardHwRev $boardBuildRev +} + +#Get BMC Board Version and Type +function _i2c_bmc_board_type_get { + # read input port 1 value from io expander + input_reg=${REG_PORT1_IN} + boardType=`i2cget -y ${I2C_BUS_BMC_HW_ID} ${I2C_ADDR_MUX_9555_12} ${input_reg}` + boardBuildRev=$((($boardType) & 0x03)) + boardHwRev=$((($boardType) >> 2 & 0x03)) + boardId=$((($boardType) >> 4)) + printf "BMC_BOARD BOARD_ID is 0x%02x, HW Rev %d, Build Rev %d\n" $boardId $boardHwRev $boardBuildRev +} + +#Get PSU Status +function _i2c_psu_status { + local psu_abs="" + + psu1PwGood=`cat ${PATH_SYSFS_PSU1}/psu_pg` + psu_abs=`cat ${PATH_SYSFS_PSU1}/psu_abs` + if [ "$psu_abs" == "0" ]; then + psu1Exist=1 + else + psu1Exist=0 + fi + + psu2PwGood=`cat ${PATH_SYSFS_PSU2}/psu_pg` + psu_abs=`cat ${PATH_SYSFS_PSU2}/psu_abs` + if [ "$psu_abs" == "0" ]; then + psu2Exist=1 + else + psu2Exist=0 + fi + + printf "PSU1 Exist:%x PSU1 PW Good:%d\n" $psu1Exist $psu1PwGood + printf "PSU2 Exist:%d PSU2 PW Good:%d\n" $psu2Exist $psu2PwGood +} + +#util functions +function _util_i2cset { + if [ "$DEBUG" == "on" ]; then + i2cset $@ + else + i2cset $@ 1>/dev/null + fi +} + +function _util_rmmod { + local mod=$1 + [ "$(lsmod | grep "^$mod ")" != "" ] && rmmod $mod +} + +# get qsfp presence +function _util_get_qsfp_abs { + _qsfp_port_i2c_var_set ${QSFP_PORT} + + #status: 0 -> Down, 1 -> Up (ACTIVE_LOW_EN) + status=`cat /sys/class/gpio/gpio$(( $(($gpioBase + (${QSFP_PORT} - 1) % 16 )) ))/value` +} + +# gpio init util function +function _util_gpio_export { + local gpio_n=$1 + local direction=$2 + local active_low=$3 + local value=$4 + + if [ -z "${gpio_n}" ]; then + echo "[gpio_init] gpio_n(${gpio_n}) is not provided" + return + fi + if [[ ${gpio_n} < 0 || ${gpio_n} > 255 ]]; then + echo "[gpio_init] gpio_n(${gpio_n}) is invalid value" + return + fi + + #export gpio + echo ${gpio_n} > /sys/class/gpio/export + #set gpio direction + echo ${direction} > /sys/class/gpio/gpio${gpio_n}/direction + #set gpio active_low + echo ${active_low} > /sys/class/gpio/gpio${gpio_n}/active_low + #set value + if [ ! -z "${value}" ]; then + echo ${value} > /sys/class/gpio/gpio${gpio_n}/value + fi +} + +# valid input number +function _util_input_check { + # input parameter validation + if [[ $1 -lt $2 || $1 -gt $3 ]]; then + echo "Please input number $2~$3" + exit + fi +} + +#Increase read socket buffer for CoPP Test +function _config_rmem { + echo "109430400" > /proc/sys/net/core/rmem_max +} + +#Main Function +function _main { + tart_time_str=`date` + start_time_sec=$(date +%s) + + if [ "${EXEC_FUNC}" == "help" ]; then + _help + elif [ "${EXEC_FUNC}" == "i2c_init" ]; then + _i2c_init + elif [ "${EXEC_FUNC}" == "i2c_deinit" ]; then + _i2c_deinit + elif [ "${EXEC_FUNC}" == "i2c_fan_speed_init" ]; then + _i2c_fan_speed_init + elif [ "${EXEC_FUNC}" == "i2c_io_exp_init" ]; then + _i2c_io_exp_init + elif [ "${EXEC_FUNC}" == "i2c_gpio_init" ]; then + _i2c_gpio_init + elif [ "${EXEC_FUNC}" == "i2c_gpio_deinit" ]; then + _i2c_gpio_deinit + elif [ "${EXEC_FUNC}" == "i2c_led_test" ]; then + _i2c_led_test + elif [ "${EXEC_FUNC}" == "i2c_mb_eeprom_get" ]; then + _i2c_mb_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_cb_eeprom_get" ]; then + _i2c_cb_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_eeprom_sync" ]; then + _i2c_eeprom_sync + elif [ "${EXEC_FUNC}" == "i2c_psu_eeprom_get" ]; then + _i2c_psu_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_eeprom_get" ]; then + _i2c_qsfp_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_eeprom_init" ]; then + _i2c_qsfp_eeprom_init ${QSFP_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_mb_eeprom_init" ]; then + _i2c_mb_eeprom_init ${MB_EEPROM_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_cb_eeprom_init" ]; then + _i2c_cb_eeprom_init ${MB_EEPROM_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_qsfp_status_get" ]; then + _i2c_qsfp_status_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_type_get" ]; then + _i2c_qsfp_type_get + elif [ "${EXEC_FUNC}" == "i2c_led_psu_status_set" ]; then + _i2c_led_psu_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_status_set" ]; then + _i2c_led_fan_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_tray_status_set" ]; then + _i2c_led_fan_tray_status_set + elif [ "${EXEC_FUNC}" == "i2c_sys_led" ]; then + _i2c_sys_led + elif [ "${EXEC_FUNC}" == "i2c_fan_led" ]; then + _i2c_fan_led + elif [ "${EXEC_FUNC}" == "i2c_fan_tray_led" ]; then + _i2c_fan_tray_led + elif [ "${EXEC_FUNC}" == "i2c_psu1_led" ]; then + _i2c_psu1_led + elif [ "${EXEC_FUNC}" == "i2c_psu2_led" ]; then + _i2c_psu2_led + elif [ "${EXEC_FUNC}" == "i2c_board_type_get" ]; then + _i2c_board_type_get + elif [ "${EXEC_FUNC}" == "i2c_bmc_board_type_get" ]; then + _i2c_bmc_board_type_get + elif [ "${EXEC_FUNC}" == "i2c_psu_status" ]; then + _i2c_psu_status + elif [ "${EXEC_FUNC}" == "i2c_test_all" ]; then + _i2c_init + _i2c_led_test + _i2c_psu_eeprom_get + _i2c_mb_eeprom_get + _i2c_cb_eeprom_get + _i2c_board_type_get + _i2c_bmc_board_type_get + _i2c_psu_status + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + if [ "$DEBUG" == "on" ]; then + echo "-----------------------------------------------------" + end_time_str=`date` + end_time_sec=$(date +%s) + diff_time=$[ ${end_time_sec} - ${start_time_sec} ] + echo "Start Time: ${start_time_str} (${start_time_sec})" + echo "End Time : ${end_time_str} (${end_time_sec})" + echo "Total Execution Time: ${diff_time} sec" + + echo "done!!!" + fi +} + +_main diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/utils/qsfp_monitor.sh b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/utils/qsfp_monitor.sh new file mode 100644 index 000000000000..249f179216a6 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/utils/qsfp_monitor.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# Copyright (C) 2017 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=3 +I2C_UTILS="/usr/sbin/i2c_utils.sh" +QSFP_SI_SCRIPT="/usr/sbin/qsfp_si_cfg.sh" +QSFP_ARRAY=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + +#QSFP SI monitor +function _qsfp_si_monitor { + local i + local status + for i in {0..31}; + do + status=`${I2C_UTILS} i2c_qsfp_status_get $(expr $i + 1) | egrep '^status=.*$' | sed -e 's/status=//g'` + if [ "${status}" == "1" ]; then + _qsfp_type_check $i + fi + done +} + +#QSFP type +function _qsfp_type_check { + local port=$1 + local qsfp_type=`${I2C_UTILS} i2c_qsfp_type_get $(expr $port + 1)` + local identifier=`echo "$qsfp_type" | grep '^identifier=.*$' | sed -e 's/identifier=//g'` + if [ "${identifier}" == "11" ]; then + connector=`echo "$qsfp_type" | grep '^connector=.*$' | sed -e 's/connector=//g'` + case ${connector} in + 21|23) + #DAC + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to DAC" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} dac $port >/dev/null + fi + ;; + *) + #Optical + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to Optical" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} optical $port >/dev/null + fi + ;; + esac + fi +} + +#Docker exist check +function _docker_swss_check { + while true + do + # Check if syncd starts + result=`docker exec -i swss bash -c "echo -en \"SELECT 1\\nHLEN HIDDEN\" | redis-cli | sed -n 2p"` #TBD FIX ME + if [ "$result" == "3" ]; then + return + fi + sleep $INTERVAL + done +} + +#Docker exist check +function _qsfp_si_cfg_script_check { + + if [ -f ${QSFP_SI_SCRIPT} ] && [ -x ${QSFP_SI_SCRIPT} ]; then + echo "SI Script exists. Start monitor." + return + else + echo "SI Script not exist. Exit monitor." + exit + fi +} + +# main function +function _main { + #Check SI Script + _qsfp_si_cfg_script_check + #Check docker swss is running + _docker_swss_check + while true + do + _qsfp_si_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main + diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/utils/s9130_32x_monitor.sh b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/utils/s9130_32x_monitor.sh new file mode 100644 index 000000000000..974da6d5001f --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9130-32x/utils/s9130_32x_monitor.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=5 +I2C_UTILS="/usr/sbin/i2c_utils.sh" + + +# LED status monitor +function _led_monitor { + ${I2C_UTILS} i2c_led_fan_status_set >/dev/null + ${I2C_UTILS} i2c_led_psu_status_set >/dev/null + ${I2C_UTILS} i2c_led_fan_tray_status_set >/dev/null +} + +# main function +function _main { + while true + do + _led_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main \ No newline at end of file diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/README.md b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/README.md new file mode 100644 index 000000000000..d1a2d5464aee --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/README.md @@ -0,0 +1,182 @@ +# Ingrasys S9230-64X Platform Driver for SONiC + +Copyright (C) 2016 Ingrasys, Inc. + +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 3 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, see . + + +## Licensing terms + +The Licensing terms of the files within this project is split 2 parts. +The Linux kernel is released under the GNU General Public License version 2. +All other code is released under the GNU General Public License version 3. +Please see the LICENSE file for copies of both licenses. + +## Contents of this package + + - service/ + > Service config files for platform initialization and monitoring + - utils/ + > Scripts useful during platform bringup and sysfs function + - conf/ + > Platform configure files. + +## Kernel modules and drivers + +The driver for interacting with Ingrasys S9230-64X is contained in the I2C +kernel module and initialization script. The initialization script loads +the modules in the correct order. It has been built and tested against +the Linux 3.16 kernel. + +The initialization script will modprobe the needed modules, navigate to the +module's device directory in sysfs, and write configuration data to +the kernel module. + +### IGB + +This is OOB Ports on front panel for management plane. + +The IGB module must be loaded first on Ingrasys S9230-64X platform. + +### I2C i801 + +The I2C i801 on Ingrasys S9230-64X can be found in +`/sys/bus/i2c/devices/i2c-0/` + +This is I2C bus for power monitor, FAN controller, HWM and thermal sensors. + +### I2C PCA9548 +The PCA9548 module on S9230-64X can be found in +`/sys/bus/i2c/devices/i2c-1/` , `/sys/bus/i2c/devices/i2c-2/`, +`/sys/bus/i2c/devices/i2c-3/`, `/sys/bus/i2c/devices/i2c-4/`, +`/sys/bus/i2c/devices/i2c-5/`, `/sys/bus/i2c/devices/i2c-6/`, +`/sys/bus/i2c/devices/i2c-7/`, `/sys/bus/i2c/devices/i2c-8/` + +The pca9548 module for CPLD get/set functions, PSU information, +fan status and EEPROM. + +## Hardware components + +The hardware components are initialized in the init script on S9230-64X. +The following describes manual initialization as well as interaction. +The examples below are just for Ingrasys S9230-64X platform. + +### Hardware initialization + +When the sonic-platform-ingrasys-s9230-64X package is installed on the S9230-64X, +it is automatically initialized. If you want to manual initialization, the +utility command usage as follows: +``` + i2c_utils.sh i2c_init +``` + +### EEPROM + +The EEPROM is including the board SKU, model name, vendor name, serial number, +and other information can be accessed with the specific eeprom kernel module. +After using `modprobe eeprom_mb` to detect the eeprom, it will show up in sysfs. + +The hexdump utility can be used to decode the raw output of the EEPROM. +For example, +``` + bash# echo "mb_eeprom 0x55" > /sys/bus/i2c/devices/i2c-0/new_device + bash# cat /sys/bus/i2c/drivers/mb_eeprom/0-0055/eeprom | hexdump -C +``` + +### Front panel LEDs + +LEDs can be setup on/off by using i2c utility `/usr/sbin/i2c_utils.sh`. +Utility function command usage as follows: + +``` +Status LED: + i2c_utils.sh i2c_sys_led green|amber + +Fan status LED: + i2c_utils.sh i2c_fan_led green|amber on|off + +PSU1 status LED: + i2c_utils.sh i2c_psu1_led green|amber on|off + +PSU2 status LED: + i2c_utils.sh i2c_psu2_led green|amber on|off + +``` +QSFP Module Port LEDs control by ASIC library. + + +### Fan speed + +Fan speed are controlled by the w83795 kernel module. +It can be found in `/sys/class/hwmon/hwmon7/device/`. +If they were compiled as modules, you will need to modprobe w83795 for +their sysfs entries to show up. Each fan has an `fan_input` file +for reading the fan speed. And `pwm1` setting fan1 to fan4, +`pwm2` setting fan5 to fan8. + +There is docker-platform-monitor container installed fancontrol package that can +automatic control platform fan speed. + + +### Temperature sensors + +There is docker-platform-monitor container installed lm-sensors package that can +monitor platform temperature. And `sensors` command can show each +temperature sensors status. + +### Power supplies + +Power supplies status and its EEPROM info can be used i2c utility +`/usr/sbin/i2c_utils.sh` to get. +The usage as follows: +``` +PSU EEPROM: + i2c_utils.sh i2c_psu_eeprom_get + hexdump -C psu0.rom + hexdump -C psu1.rom + +PSU Status: + i2c_utils.sh i2c_psu_status +``` + +### QSFPs +QSFP modules are managed by the CPLD 1~5 according to port number. +The i2c utility `/usr/sbin/i2c_utils.sh` can be used to get status and +module EEPROM informations. +The usage as follows: +``` +QSFP EEPROM: + i2c_utils.sh i2c_qsfp_eeprom_get [1-64] + +QSFP Insert Event: + i2c_utils.sh i2c_qsfp_status_get [1-64] + 0 => not insert + 1 => inserted +``` + +### SFP+ +SFP+ modules are managed by the CPLD 1~2 according to port number. +The i2c utility `/usr/sbin/i2c_utils.sh` can be used to get status and +module EEPROM informations. +The usage as follows: +``` +SFP EEPROM: + i2c_utils.sh i2c_sfp_eeprom_get [1-2] + +SFP Insert Event: + i2c_utils.sh i2c_sfp_status_get [1-2] + 0 => not insert + 1 => inserted +``` + diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/Makefile b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/Makefile new file mode 100755 index 000000000000..3107674e8712 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/Makefile @@ -0,0 +1,3 @@ +obj-m := eeprom_mb.o +obj-m += ingrasys_s9230_64x_i2c_cpld.o +obj-m += ingrasys_s9230_64x_psu.o diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/eeprom_mb.c b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/eeprom_mb.c new file mode 100755 index 000000000000..528864d93382 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/eeprom_mb.c @@ -0,0 +1,267 @@ +/* + * Copyright (C) 1998, 1999 Frodo Looijaard and + * Philip Edelbrock + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003 IBM Corp. + * Copyright (C) 2004 Jean Delvare + * + * 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. + */ + +/* enable dev_dbg print out */ +//#define DEBUG + +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { /*0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57,*/ I2C_CLIENT_END }; + +/* Size of EEPROM in bytes */ +#define EEPROM_SIZE 512 + +#define SLICE_BITS (6) +#define SLICE_SIZE (1 << SLICE_BITS) +#define SLICE_NUM (EEPROM_SIZE/SLICE_SIZE) + +/* Each client has this additional data */ +struct eeprom_data { + struct mutex update_lock; + u8 valid; /* bitfield, bit!=0 if slice is valid */ + unsigned long last_updated[SLICE_NUM]; /* In jiffies, 8 slices */ + u8 data[EEPROM_SIZE]; /* Register values */ +}; + + +static void mb_eeprom_update_client(struct i2c_client *client, u8 slice) +{ + struct eeprom_data *data = i2c_get_clientdata(client); + int i, j; + int ret; + int addr; + + mutex_lock(&data->update_lock); + + if (!(data->valid & (1 << slice)) || + time_after(jiffies, data->last_updated[slice] + 300 * HZ)) { + dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice); + + addr = slice << SLICE_BITS; + + ret = i2c_smbus_write_byte_data(client, (u8)((addr >> 8) & 0xFF), (u8)(addr & 0xFF)); + /* select the eeprom address */ + if (ret < 0) { + dev_err(&client->dev, "address set failed\n"); + goto exit; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE)) { + goto exit; + } + + for (i = slice << SLICE_BITS; i < (slice + 1) << SLICE_BITS; i+= SLICE_SIZE) { + for (j = i; j < (i+SLICE_SIZE); j++) { + int res; + + res = i2c_smbus_read_byte(client); + if (res < 0) { + goto exit; + } + + data->data[j] = res & 0xFF; + } + } + + data->last_updated[slice] = jiffies; + data->valid |= (1 << slice); + } +exit: + mutex_unlock(&data->update_lock); +} + +static ssize_t mb_eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct eeprom_data *data = i2c_get_clientdata(client); + u8 slice; + + if (off > EEPROM_SIZE) { + return 0; + } + if (off + count > EEPROM_SIZE) { + count = EEPROM_SIZE - off; + } + if (count == 0) { + return 0; + } + + /* Only refresh slices which contain requested bytes */ + for (slice = off >> SLICE_BITS; slice <= (off + count - 1) >> SLICE_BITS; slice++) { + mb_eeprom_update_client(client, slice); + } + + memcpy(buf, &data->data[off], count); + + return count; +} + +static ssize_t mb_eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct eeprom_data *data = i2c_get_clientdata(client); + int ret; + int i; + u8 cmd; + u16 value16; + + dev_dbg(&client->dev, "mb_eeprom_write off=%d, count=%d\n", (int)off, (int)count); + + if (off > EEPROM_SIZE) { + return 0; + } + if (off + count > EEPROM_SIZE) { + count = EEPROM_SIZE - off; + } + if (count == 0) { + return 0; + } + + mutex_lock(&data->update_lock); + + for(i=0; i < count; i++) { + /* write command */ + cmd = (off >> 8) & 0xff; + value16 = off & 0xff; + value16 |= buf[i] << 8; + ret = i2c_smbus_write_word_data(client, cmd, value16); + + if (ret < 0) { + dev_err(&client->dev, "write address failed at %d \n", (int)off); + goto exit; + } + + off++; + + /* need to wait for write complete */ + udelay(10000); + } +exit: + mutex_unlock(&data->update_lock); + /* force to update client when reading */ + for(i=0; i < SLICE_NUM; i++) { + data->last_updated[i] = 0; + } + + return count; +} + +static struct bin_attribute mb_eeprom_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO | S_IWUSR, + }, + .size = EEPROM_SIZE, + .read = mb_eeprom_read, + .write = mb_eeprom_write, +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int mb_eeprom_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all + addresses 0x50-0x57, but we only care about 0x51 and 0x55. So decline + attaching to addresses >= 0x56 on DDC buses */ + if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x56) { + return -ENODEV; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE) + && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + return -ENODEV; + } + + strlcpy(info->type, "eeprom", I2C_NAME_SIZE); + + return 0; +} + +static int mb_eeprom_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct eeprom_data *data; + int err; + + if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + memset(data->data, 0xff, EEPROM_SIZE); + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &mb_eeprom_attr); + if (err) { + goto exit_kfree; + } + + return 0; + +exit_kfree: + kfree(data); +exit: + return err; +} + +static int mb_eeprom_remove(struct i2c_client *client) +{ + sysfs_remove_bin_file(&client->dev.kobj, &mb_eeprom_attr); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id mb_eeprom_id[] = { + { "mb_eeprom", 0 }, + { } +}; + +static struct i2c_driver mb_eeprom_driver = { + .driver = { + .name = "mb_eeprom", + }, + .probe = mb_eeprom_probe, + .remove = mb_eeprom_remove, + .id_table = mb_eeprom_id, + + .class = I2C_CLASS_DDC | I2C_CLASS_SPD, + .detect = mb_eeprom_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(mb_eeprom_driver); + +MODULE_AUTHOR("Wade "); +MODULE_DESCRIPTION("Ingrasys Mother Borad EEPROM driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/ingrasys_s9230_64x_i2c_cpld.c b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/ingrasys_s9230_64x_i2c_cpld.c new file mode 100644 index 000000000000..7d832fc0149f --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/ingrasys_s9230_64x_i2c_cpld.c @@ -0,0 +1,1259 @@ +/* + * A i2c cpld driver for the ingrasys_s9230_64x + * + * Copyright (C) 2017 Ingrasys Technology Corporation. + * Leo Lin + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ingrasys_s9230_64x_i2c_cpld.h" + +#ifdef DEBUG +#define DEBUG_PRINT(fmt, args...) \ + printk(KERN_INFO "%s:%s[%d]: " fmt "\r\n", \ + __FILE__, __func__, __LINE__, ##args) +#else +#define DEBUG_PRINT(fmt, args...) +#endif + +#define I2C_READ_BYTE_DATA(ret, lock, i2c_client, reg) \ +{ \ + mutex_lock(lock); \ + ret = i2c_smbus_read_byte_data(i2c_client, reg); \ + mutex_unlock(lock); \ +} +#define I2C_WRITE_BYTE_DATA(ret, lock, i2c_client, reg, val) \ +{ \ + mutex_lock(lock); \ + ret = i2c_smbus_write_byte_data(i2c_client, reg, val); \ + mutex_unlock(lock); \ +} + +/* CPLD sysfs attributes index */ +enum s9230_64x_cpld_sysfs_attributes { + CPLD_ACCESS_REG, + CPLD_REGISTER_VAL, + CPLD_PORT_START, + CPLD_PORTS, + CPLD_VERSION, + CPLD_ID, + CPLD_BOARD_TYPE, + CPLD_EXT_BOARD_TYPE, + CPLD_QSFP_PORT_STATUS_1, + CPLD_QSFP_PORT_STATUS_2, + CPLD_QSFP_PORT_STATUS_3, + CPLD_QSFP_PORT_STATUS_4, + CPLD_QSFP_PORT_STATUS_5, + CPLD_QSFP_PORT_STATUS_6, + CPLD_QSFP_PORT_STATUS_7, + CPLD_QSFP_PORT_STATUS_8, + CPLD_QSFP_PORT_STATUS_9, + CPLD_QSFP_PORT_STATUS_10, + CPLD_QSFP_PORT_STATUS_11, + CPLD_QSFP_PORT_STATUS_12, + CPLD_QSFP_PORT_STATUS_13, + CPLD_QSFP_PORT_CONFIG_1, + CPLD_QSFP_PORT_CONFIG_2, + CPLD_QSFP_PORT_CONFIG_3, + CPLD_QSFP_PORT_CONFIG_4, + CPLD_QSFP_PORT_CONFIG_5, + CPLD_QSFP_PORT_CONFIG_6, + CPLD_QSFP_PORT_CONFIG_7, + CPLD_QSFP_PORT_CONFIG_8, + CPLD_QSFP_PORT_CONFIG_9, + CPLD_QSFP_PORT_CONFIG_10, + CPLD_QSFP_PORT_CONFIG_11, + CPLD_QSFP_PORT_CONFIG_12, + CPLD_QSFP_PORT_CONFIG_13, + CPLD_QSFP_PORT_INTERRUPT, + CPLD_SFP_PORT_STATUS, + CPLD_SFP_PORT_CONFIG, + CPLD_10GMUX_CONFIG, + CPLD_BMC_STATUS, + CPLD_BMC_WATCHDOG, + CPLD_USB_STATUS, +}; + +/* CPLD sysfs attributes hook functions */ +static ssize_t read_access_register(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t write_access_register(struct device *dev, + struct device_attribute *da, const char *buf, size_t count); +static ssize_t read_register_value(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t write_register_value(struct device *dev, + struct device_attribute *da, const char *buf, size_t count); +static ssize_t get_qsfp_port_start(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t get_qsfp_ports(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_cpld_version(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_cpld_id(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_board_type(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_ext_board_type(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_qsfp_port_status(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_qsfp_port_config(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t write_qsfp_port_config(struct device *dev, + struct device_attribute *da, const char *buf, size_t count); +static ssize_t read_qsfp_port_interrupt(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_sfp_port_status(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_sfp_port_config(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t write_sfp_port_config(struct device *dev, + struct device_attribute *da, const char *buf, size_t count); +static ssize_t read_10gmux_config(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t write_10gmux_config(struct device *dev, + struct device_attribute *da, const char *buf, size_t count); +static ssize_t read_bmc_status(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t read_bmc_watchdog(struct device *dev, + struct device_attribute *da, char *buf); +static ssize_t write_bmc_watchdog(struct device *dev, + struct device_attribute *da, const char *buf, size_t count); +static ssize_t read_usb_status(struct device *dev, + struct device_attribute *da, char *buf); + +static LIST_HEAD(cpld_client_list); /* client list for cpld */ +static struct mutex list_lock; /* mutex for client list */ + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +struct cpld_data { + int index; /* CPLD index */ + struct mutex access_lock; /* mutex for cpld access */ + u8 access_reg; /* register to access */ +}; + +/* CPLD device id and data */ +static const struct i2c_device_id ingrasys_i2c_cpld_id[] = { + { "ingrasys_cpld1", cpld1 }, + { "ingrasys_cpld2", cpld2 }, + { "ingrasys_cpld3", cpld3 }, + { "ingrasys_cpld4", cpld4 }, + { "ingrasys_cpld5", cpld5 }, + {} +}; + +/* Addresses scanned for ingrasys_i2c_cpld */ +static const unsigned short cpld_i2c_addr[] = { 0x33, I2C_CLIENT_END }; + +/* define all support register access of cpld in attribute */ +static SENSOR_DEVICE_ATTR(cpld_access_register, S_IWUSR | S_IRUGO, + read_access_register, write_access_register, CPLD_ACCESS_REG); +static SENSOR_DEVICE_ATTR(cpld_register_value, S_IWUSR | S_IRUGO, + read_register_value, write_register_value, CPLD_REGISTER_VAL); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_start, S_IRUGO, + get_qsfp_port_start, NULL, CPLD_PORT_START); +static SENSOR_DEVICE_ATTR(cpld_qsfp_ports, S_IRUGO, + get_qsfp_ports, NULL, CPLD_PORTS); +static SENSOR_DEVICE_ATTR(cpld_version, S_IRUGO, + read_cpld_version, NULL, CPLD_VERSION); +static SENSOR_DEVICE_ATTR(cpld_id, S_IRUGO, read_cpld_id, NULL, CPLD_ID); +static SENSOR_DEVICE_ATTR(cpld_board_type, S_IRUGO, + read_board_type, NULL, CPLD_BOARD_TYPE); +static SENSOR_DEVICE_ATTR(cpld_ext_board_type, S_IRUGO, + read_ext_board_type, NULL, CPLD_EXT_BOARD_TYPE); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_1, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_1); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_2, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_2); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_3, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_3); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_4, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_4); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_5, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_5); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_6, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_6); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_7, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_7); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_8, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_8); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_9, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_9); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_10, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_10); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_11, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_11); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_12, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_12); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_status_13, S_IRUGO, + read_qsfp_port_status, NULL, CPLD_QSFP_PORT_STATUS_13); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_1, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, CPLD_QSFP_PORT_CONFIG_1); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_2, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, + CPLD_QSFP_PORT_CONFIG_2); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_3, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, + CPLD_QSFP_PORT_CONFIG_3); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_4, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, + CPLD_QSFP_PORT_CONFIG_4); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_5, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, + CPLD_QSFP_PORT_CONFIG_5); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_6, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, + CPLD_QSFP_PORT_CONFIG_6); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_7, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, + CPLD_QSFP_PORT_CONFIG_7); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_8, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, + CPLD_QSFP_PORT_CONFIG_8); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_9, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, + CPLD_QSFP_PORT_CONFIG_9); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_10, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, + CPLD_QSFP_PORT_CONFIG_10); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_11, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, + CPLD_QSFP_PORT_CONFIG_11); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_12, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, + CPLD_QSFP_PORT_CONFIG_12); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_config_13, S_IWUSR | S_IRUGO, + read_qsfp_port_config, write_qsfp_port_config, + CPLD_QSFP_PORT_CONFIG_13); +static SENSOR_DEVICE_ATTR(cpld_qsfp_port_interrupt, S_IRUGO, + read_qsfp_port_interrupt, NULL, CPLD_QSFP_PORT_INTERRUPT); +static SENSOR_DEVICE_ATTR(cpld_sfp_port_status, S_IRUGO, + read_sfp_port_status, NULL, CPLD_SFP_PORT_STATUS); +static SENSOR_DEVICE_ATTR(cpld_sfp_port_config, S_IWUSR | S_IRUGO, + read_sfp_port_config, write_sfp_port_config, CPLD_SFP_PORT_CONFIG); +static SENSOR_DEVICE_ATTR(cpld_10gmux_config, S_IWUSR | S_IRUGO, + read_10gmux_config, write_10gmux_config, + CPLD_10GMUX_CONFIG); +static SENSOR_DEVICE_ATTR(cpld_bmc_status, S_IRUGO, + read_bmc_status, NULL, CPLD_BMC_STATUS); +static SENSOR_DEVICE_ATTR(cpld_bmc_watchdog, S_IWUSR | S_IRUGO, + read_bmc_watchdog, write_bmc_watchdog, + CPLD_BMC_WATCHDOG); +static SENSOR_DEVICE_ATTR(cpld_usb_status, S_IRUGO, + read_usb_status, NULL, CPLD_USB_STATUS); + + +/* define support attributes of cpldx , total 5 */ +/* cpld 1 */ +static struct attribute *s9230_64x_cpld1_attributes[] = { + &sensor_dev_attr_cpld_access_register.dev_attr.attr, + &sensor_dev_attr_cpld_register_value.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_start.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_ports.dev_attr.attr, + &sensor_dev_attr_cpld_version.dev_attr.attr, + &sensor_dev_attr_cpld_id.dev_attr.attr, + &sensor_dev_attr_cpld_board_type.dev_attr.attr, + &sensor_dev_attr_cpld_ext_board_type.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_1.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_2.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_3.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_4.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_5.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_6.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_7.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_8.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_9.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_10.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_11.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_12.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_1.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_2.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_3.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_4.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_5.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_6.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_7.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_8.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_9.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_10.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_11.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_12.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_interrupt.dev_attr.attr, + &sensor_dev_attr_cpld_sfp_port_status.dev_attr.attr, + &sensor_dev_attr_cpld_sfp_port_config.dev_attr.attr, + &sensor_dev_attr_cpld_10gmux_config.dev_attr.attr, + &sensor_dev_attr_cpld_bmc_status.dev_attr.attr, + &sensor_dev_attr_cpld_bmc_watchdog.dev_attr.attr, + &sensor_dev_attr_cpld_usb_status.dev_attr.attr, + NULL +}; +/* cpld 2 */ +static struct attribute *s9230_64x_cpld2_attributes[] = { + &sensor_dev_attr_cpld_access_register.dev_attr.attr, + &sensor_dev_attr_cpld_register_value.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_start.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_ports.dev_attr.attr, + &sensor_dev_attr_cpld_version.dev_attr.attr, + &sensor_dev_attr_cpld_id.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_1.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_2.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_3.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_4.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_5.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_6.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_7.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_8.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_9.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_10.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_11.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_12.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_13.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_1.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_2.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_3.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_4.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_5.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_6.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_7.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_8.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_9.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_10.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_11.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_12.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_13.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_interrupt.dev_attr.attr, + &sensor_dev_attr_cpld_sfp_port_status.dev_attr.attr, + &sensor_dev_attr_cpld_sfp_port_config.dev_attr.attr, + NULL +}; +/* cpld 3 / cpld 4 / cpld 5 */ +static struct attribute *s9230_64x_cpld345_attributes[] = { +&sensor_dev_attr_cpld_access_register.dev_attr.attr, + &sensor_dev_attr_cpld_register_value.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_start.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_ports.dev_attr.attr, + &sensor_dev_attr_cpld_version.dev_attr.attr, + &sensor_dev_attr_cpld_id.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_1.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_2.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_3.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_4.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_5.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_6.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_7.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_8.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_9.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_10.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_11.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_12.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_status_13.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_1.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_2.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_3.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_4.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_5.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_6.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_7.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_8.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_9.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_10.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_11.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_12.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_config_13.dev_attr.attr, + &sensor_dev_attr_cpld_qsfp_port_interrupt.dev_attr.attr, + NULL +}; + +/* cpld 1 attributes group */ +static const struct attribute_group s9230_64x_cpld1_group = { + .attrs = s9230_64x_cpld1_attributes, +}; +/* cpld 2 attributes group */ +static const struct attribute_group s9230_64x_cpld2_group = { + .attrs = s9230_64x_cpld2_attributes, +}; +/* cpld 3/4/5 attributes group */ +static const struct attribute_group s9230_64x_cpld345_group = { + .attrs = s9230_64x_cpld345_attributes, +}; + +/* read access register from cpld data */ +static ssize_t read_access_register(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg = data->access_reg; + + return sprintf(buf, "0x%x\n", reg); +} + +/* write access register to cpld data */ +static ssize_t write_access_register(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + + if (kstrtou8(buf, 0, ®) < 0) + return -EINVAL; + + data->access_reg = reg; + return count; +} + +/* read the value of access register in cpld data */ +static ssize_t read_register_value(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg = data->access_reg; + int reg_val; + + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + + if (reg_val < 0) + return -1; + + return sprintf(buf, "0x%x\n", reg_val); +} + +/* wrtie the value to access register in cpld data */ +static ssize_t write_register_value(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + int ret = -EIO; + u8 reg = data->access_reg; + u8 reg_val; + + if (kstrtou8(buf, 0, ®_val) < 0) + return -EINVAL; + + I2C_WRITE_BYTE_DATA(ret, &data->access_lock, client, reg, reg_val); + + return count; +} + +/* get qsfp port start number of the cpld device */ +/* the start number use to tranlate qsfp port to cpld port */ +/* the cpld port use to access the qsfp port register in cpld */ +static ssize_t get_qsfp_port_start(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + int port_base_num; + + if (attr->index == CPLD_PORT_START) { + if (data->index == cpld1) { + port_base_num = 1; + } else { + port_base_num = CPLD_1_PORT_NUM + + CPLD_2_PORT_NUM*(data->index - 1) + 1; + } + return sprintf(buf, "%d\n", port_base_num); + } + return -1; +} + +/* get total qsfp port which contain register in the cpld device */ +static ssize_t get_qsfp_ports(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + int ports; + + if (attr->index == CPLD_PORTS) { + if (data->index == cpld1) + ports = CPLD_1_PORT_NUM; + else + ports = CPLD_2_PORT_NUM; + return sprintf(buf, "%d\n", ports); + } + return -1; +} + +/* get cpdl version register value */ +static ssize_t read_cpld_version(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_VERSION) { + reg = CPLD_VERSION_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get cpdl id register value */ +static ssize_t read_cpld_id(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_ID) { + reg = CPLD_ID_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get board type register value */ +static ssize_t read_board_type(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_BOARD_TYPE) { + reg = CPLD_BOARD_TYPE_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get extend board type register value */ +static ssize_t read_ext_board_type(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_EXT_BOARD_TYPE) { + reg = CPLD_EXT_BOARD_TYPE_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get qsfp port status register value */ +static ssize_t read_qsfp_port_status(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index >= CPLD_QSFP_PORT_STATUS_1 && + attr->index <= CPLD_QSFP_PORT_STATUS_13) { + reg = CPLD_QSFP_PORT_STATUS_BASE_REG + + (attr->index - CPLD_QSFP_PORT_STATUS_1); + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get qsfp port config register value */ +static ssize_t read_qsfp_port_config(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index >= CPLD_QSFP_PORT_CONFIG_1 && + attr->index <= CPLD_QSFP_PORT_CONFIG_13) { + reg = CPLD_QSFP_PORT_CONFIG_BASE_REG + + (attr->index - CPLD_QSFP_PORT_CONFIG_1); + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* set value to qsfp port config register */ +static ssize_t write_qsfp_port_config(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg, reg_val; + int ret; + + if (kstrtou8(buf, 0, ®_val) < 0) + return -EINVAL; + + if (attr->index >= CPLD_QSFP_PORT_CONFIG_1 && + attr->index <= CPLD_QSFP_PORT_CONFIG_13) { + reg = CPLD_QSFP_PORT_CONFIG_BASE_REG + + (attr->index - CPLD_QSFP_PORT_CONFIG_1); + I2C_WRITE_BYTE_DATA(ret, &data->access_lock, + client, reg, reg_val); + } + return count; +} + +/* get qsfp port interrupt register value */ +static ssize_t read_qsfp_port_interrupt(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_QSFP_PORT_INTERRUPT) { + reg = CPLD_QSFP_PORT_INTERRUPT_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get sfp port status register value */ +static ssize_t read_sfp_port_status(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_SFP_PORT_STATUS) { + reg = CPLD_SFP_PORT_STATUS_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get sfp port config register value */ +static ssize_t read_sfp_port_config(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_SFP_PORT_CONFIG) { + reg = CPLD_SFP_PORT_CONFIG_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* set value to sfp port config register */ +static ssize_t write_sfp_port_config(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg, reg_val; + int ret; + + if (kstrtou8(buf, 0, ®_val) < 0) + return -EINVAL; + + if (attr->index == CPLD_SFP_PORT_CONFIG) { + reg = CPLD_SFP_PORT_CONFIG_REG; + I2C_WRITE_BYTE_DATA(ret, &data->access_lock, + client, reg, reg_val); + } + return count; +} + +/* get 10g mux config register value */ +static ssize_t read_10gmux_config(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_10GMUX_CONFIG) { + reg = CPLD_10GMUX_CONFIG_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* set value to 10g mux config register */ +static ssize_t write_10gmux_config(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg, reg_val; + int ret; + + if (kstrtou8(buf, 0, ®_val) < 0) + return -EINVAL; + + if (attr->index == CPLD_10GMUX_CONFIG) { + reg = CPLD_10GMUX_CONFIG_REG; + I2C_WRITE_BYTE_DATA(ret, &data->access_lock, + client, reg, reg_val); + } + return count; +} + +/* get bmc status register value */ +static ssize_t read_bmc_status(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_BMC_STATUS) { + reg = CPLD_BMC_STATUS_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* get bmc watchdog register value */ +static ssize_t read_bmc_watchdog(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_BMC_WATCHDOG) { + reg = CPLD_BMC_WATCHDOG_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* set value to bmc watchdog register */ +static ssize_t write_bmc_watchdog(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg, reg_val; + int ret; + + if (kstrtou8(buf, 0, ®_val) < 0) + return -EINVAL; + + if (attr->index == CPLD_BMC_WATCHDOG) { + reg = CPLD_BMC_WATCHDOG_REG; + I2C_WRITE_BYTE_DATA(ret, &data->access_lock, + client, reg, reg_val); + } + return count; +} + +/* get usb status register value */ +static ssize_t read_usb_status(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct cpld_data *data = i2c_get_clientdata(client); + u8 reg; + int reg_val; + + if (attr->index == CPLD_USB_STATUS) { + reg = CPLD_USB_STATUS_REG; + I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); + if (reg_val < 0) + return -1; + return sprintf(buf, "0x%02x\n", reg_val); + } + return -1; +} + +/* add valid cpld client to list */ +static void ingrasys_i2c_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = NULL; + + node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + if (!node) { + dev_info(&client->dev, + "Can't allocate cpld_client_node for index %d\n", + client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +/* remove exist cpld client in list */ +static void ingrasys_i2c_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + list_for_each(list_node, &cpld_client_list) { + cpld_node = list_entry(list_node, + struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + mutex_unlock(&list_lock); +} + +/* cpld drvier probe */ +static int ingrasys_i2c_cpld_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + struct cpld_data *data = NULL; + int ret = -EPERM; + int err; + int idx; + + data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* init cpld data for client */ + i2c_set_clientdata(client, data); + mutex_init(&data->access_lock); + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_info(&client->dev, + "i2c_check_functionality failed (0x%x)\n", + client->addr); + status = -EIO; + goto exit; + } + + /* get cpld id from device */ + ret = i2c_smbus_read_byte_data(client, CPLD_ID_REG); + + if (ret < 0) { + dev_info(&client->dev, + "fail to get cpld id (0x%x) at addr (0x%x)\n", + CPLD_ID_REG, client->addr); + status = -EIO; + goto exit; + } + + CPLD_ID_ID_GET(ret, idx); + + if (INVALID(idx, cpld1, cpld5)) { + dev_info(&client->dev, + "cpld id %d(device) not valid\n", idx); + //status = -EPERM; + //goto exit; + } + +#if 0 + /* change client name for each cpld with index */ + snprintf(client->name, sizeof(client->name), "%s_%d", client->name, + data->index); +#endif + + data->index = dev_id->driver_data; + + /* register sysfs hooks for different cpld group */ + dev_info(&client->dev, "probe cpld with index %d\n", data->index); + switch (data->index) { + case cpld1: + status = sysfs_create_group(&client->dev.kobj, + &s9230_64x_cpld1_group); + break; + case cpld2: + status = sysfs_create_group(&client->dev.kobj, + &s9230_64x_cpld2_group); + break; + case cpld3: + case cpld4: + case cpld5: + status = sysfs_create_group(&client->dev.kobj, + &s9230_64x_cpld345_group); + break; + default: + status = -EINVAL; + } + + if (status) + goto exit; + + dev_info(&client->dev, "chip found\n"); + + /* add probe chip to client list */ + ingrasys_i2c_cpld_add_client(client); + + return 0; +exit: + sysfs_remove_group(&client->dev.kobj, &s9230_64x_cpld345_group); + return status; +} + +/* cpld drvier remove */ +static int ingrasys_i2c_cpld_remove(struct i2c_client *client) +{ + struct cpld_data *data = i2c_get_clientdata(client); + + switch (data->index) { + case cpld1: + sysfs_remove_group(&client->dev.kobj, &s9230_64x_cpld1_group); + break; + case cpld2: + sysfs_remove_group(&client->dev.kobj, &s9230_64x_cpld2_group); + break; + case cpld3: + case cpld4: + case cpld5: + sysfs_remove_group(&client->dev.kobj, + &s9230_64x_cpld345_group); + break; + } + + ingrasys_i2c_cpld_remove_client(client); + return 0; +} + +MODULE_DEVICE_TABLE(i2c, ingrasys_i2c_cpld_id); + +static struct i2c_driver ingrasys_i2c_cpld_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ingrasys_i2c_cpld", + }, + .probe = ingrasys_i2c_cpld_probe, + .remove = ingrasys_i2c_cpld_remove, + .id_table = ingrasys_i2c_cpld_id, + .address_list = cpld_i2c_addr, +}; + +/* provid cpld register read */ +/* cpld_idx indicate the index of cpld device */ +int ingrasys_i2c_cpld_read(u8 cpld_idx, + u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EPERM; + struct cpld_data *data; + + list_for_each(list_node, &cpld_client_list) { + cpld_node = list_entry(list_node, + struct cpld_client_node, list); + data = i2c_get_clientdata(cpld_node->client); + if (data->index == cpld_idx) { + DEBUG_PRINT("cpld_idx=%d, read reg 0x%02x", + cpld_idx, reg); + I2C_READ_BYTE_DATA(ret, &data->access_lock, + cpld_node->client, reg); + DEBUG_PRINT("cpld_idx=%d, read reg 0x%02x = 0x%02x", + cpld_idx, reg, ret); + break; + } + } + + return ret; +} +EXPORT_SYMBOL(ingrasys_i2c_cpld_read); + +/* provid cpld register write */ +/* cpld_idx indicate the index of cpld device */ +int ingrasys_i2c_cpld_write(u8 cpld_idx, + u8 reg, + u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + struct cpld_data *data; + + list_for_each(list_node, &cpld_client_list) { + cpld_node = list_entry(list_node, + struct cpld_client_node, list); + data = i2c_get_clientdata(cpld_node->client); + + if (data->index == cpld_idx) { + I2C_WRITE_BYTE_DATA(ret, &data->access_lock, + cpld_node->client, + reg, value); + DEBUG_PRINT("cpld_idx=%d, write reg 0x%02x val 0x%02x, ret=%d", + cpld_idx, reg, value, ret); + break; + } + } + + return ret; +} +EXPORT_SYMBOL(ingrasys_i2c_cpld_write); + +/* provid qsfp port status register read */ +/* port_num indicate the front panel qsfp port number */ +int ingrasys_i2c_cpld_get_qsfp_port_status_val(u8 port_num) +{ + u8 cpld_idx, cpld_port, reg; + int reg_val; + + if (INVALID(port_num, QSFP_MIN_PORT_NUM, QSFP_MAX_PORT_NUM)) { + DEBUG_PRINT("invalid input value %d", port_num); + return -1; + } + QSFP_TO_CPLD_IDX(port_num, cpld_idx, cpld_port); + reg = QSFP_PORT_STATUS_REG(cpld_port); + DEBUG_PRINT("port_num=%d, cpld_idx=%d, cpld_port=%d, reg=0x%x", + port_num, cpld_idx, cpld_port, reg); + reg_val = ingrasys_i2c_cpld_read(cpld_idx, reg); + return reg_val; +} +EXPORT_SYMBOL(ingrasys_i2c_cpld_get_qsfp_port_status_val); + +/* provid qsfp port config register read */ +/* port_num indicate the front panel qsfp port number */ +int ingrasys_i2c_cpld_get_qsfp_port_config_val(u8 port_num) +{ + u8 cpld_idx, cpld_port, reg; + int reg_val; + + if (INVALID(port_num, QSFP_MIN_PORT_NUM, QSFP_MAX_PORT_NUM)) { + DEBUG_PRINT("invalid input value %d", port_num); + return -1; + } + QSFP_TO_CPLD_IDX(port_num, cpld_idx, cpld_port); + reg = QSFP_PORT_CONFIG_REG(cpld_port); + DEBUG_PRINT("port_num=%d, cpld_idx=%d, cpld_port=%d, reg=0x%x", + port_num, cpld_idx, cpld_port, reg); + reg_val = ingrasys_i2c_cpld_read(cpld_idx, reg); + return reg_val; +} +EXPORT_SYMBOL(ingrasys_i2c_cpld_get_qsfp_port_config_val); + +/* provid qsfp port config register write */ +/* port_num indicate the front panel qsfp port number */ +int ingrasys_i2c_cpld_set_qsfp_port_config_val(u8 port_num, + u8 reg_val) +{ + u8 cpld_idx, cpld_port, reg, ret; + + if (INVALID(port_num, QSFP_MIN_PORT_NUM, QSFP_MAX_PORT_NUM)) { + DEBUG_PRINT("invalid input value %d", port_num); + return -1; + } + QSFP_TO_CPLD_IDX(port_num, cpld_idx, cpld_port); + reg = QSFP_PORT_CONFIG_REG(cpld_port); + DEBUG_PRINT("port_num=%d, cpld_idx=%d, cpld_port=%d, reg=0x%x", + port_num, cpld_idx, cpld_port, reg); + ret = ingrasys_i2c_cpld_write(cpld_idx, reg, reg_val); + return ret; +} +EXPORT_SYMBOL(ingrasys_i2c_cpld_set_qsfp_port_config_val); + +/* provid sfp port status register read */ +/* port_num indicate the front panel qsfp port number */ +int ingrasys_i2c_cpld_get_sfp_port_status_val(u8 port_num) +{ + u8 cpld_idx, reg; + int reg_val; + + if (INVALID(port_num, SFP_MIN_PORT_NUM, SFP_MAX_PORT_NUM)) { + DEBUG_PRINT("invalid input value %d", port_num); + return -1; + } + SFP_TO_CPLD_IDX(port_num, cpld_idx); + reg = CPLD_SFP_PORT_STATUS_REG; + DEBUG_PRINT("port_num=%d, cpld_idx=%d, reg=0x%x", + port_num, cpld_idx, reg); + reg_val = ingrasys_i2c_cpld_read(cpld_idx, reg); + return reg_val; +} +EXPORT_SYMBOL(ingrasys_i2c_cpld_get_sfp_port_status_val); + +/* provid qsfp port config register read */ +/* port_num indicate the front panel qsfp port number */ +int ingrasys_i2c_cpld_get_sfp_port_config_val(u8 port_num) +{ + u8 cpld_idx, reg; + int reg_val; + + if (INVALID(port_num, SFP_MIN_PORT_NUM, SFP_MAX_PORT_NUM)) { + DEBUG_PRINT("invalid input value %d", port_num); + return -1; + } + SFP_TO_CPLD_IDX(port_num, cpld_idx); + reg = CPLD_SFP_PORT_CONFIG_REG; + DEBUG_PRINT("port_num=%d, cpld_idx=%d, reg=0x%x", + port_num, cpld_idx, reg); + reg_val = ingrasys_i2c_cpld_read(cpld_idx, reg); + return reg_val; +} +EXPORT_SYMBOL(ingrasys_i2c_cpld_get_sfp_port_config_val); + +/* provid qsfp port config register write */ +/* port_num indicate the front panel qsfp port number */ +int ingrasys_i2c_cpld_set_sfp_port_config_val(u8 port_num, + u8 reg_val) +{ + u8 cpld_idx, reg, ret; + + if (INVALID(port_num, SFP_MIN_PORT_NUM, SFP_MAX_PORT_NUM)) { + DEBUG_PRINT("invalid input value %d", port_num); + return -1; + } + SFP_TO_CPLD_IDX(port_num, cpld_idx); + reg = CPLD_SFP_PORT_CONFIG_REG; + DEBUG_PRINT("port_num=%d, cpld_idx=%d, reg=0x%x", + port_num, cpld_idx, reg); + ret = ingrasys_i2c_cpld_write(cpld_idx, reg, reg_val); + return ret; +} +EXPORT_SYMBOL(ingrasys_i2c_cpld_set_sfp_port_config_val); + +static int __init ingrasys_i2c_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&ingrasys_i2c_cpld_driver); +} + +static void __exit ingrasys_i2c_cpld_exit(void) +{ + i2c_del_driver(&ingrasys_i2c_cpld_driver); +} + +MODULE_AUTHOR("Leo Lin "); +MODULE_DESCRIPTION("ingrasys_i2c_cpld driver"); +MODULE_LICENSE("GPL"); + +module_init(ingrasys_i2c_cpld_init); +module_exit(ingrasys_i2c_cpld_exit); + diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/ingrasys_s9230_64x_i2c_cpld.h b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/ingrasys_s9230_64x_i2c_cpld.h new file mode 100644 index 000000000000..e0e53174d675 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/ingrasys_s9230_64x_i2c_cpld.h @@ -0,0 +1,223 @@ +/* header file for i2c cpld driver of ingrasys_s9230_64x + * + * Copyright (C) 2017 Ingrasys Technology Corporation. + * Leo Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef INGRASYS_S9230_64X_I2C_CPLD_H +#define INGRASYS_S9230_64X_I2C_CPLD_H + +/* CPLD device index value */ +enum cpld_id { + cpld1, + cpld2, + cpld3, + cpld4, + cpld5 +}; + +/* port number on CPLD */ +#define CPLD_1_PORT_NUM 12 +#define CPLD_2_PORT_NUM 13 + +/* QSFP port number */ +#define QSFP_MAX_PORT_NUM 64 +#define QSFP_MIN_PORT_NUM 1 + +/* SFP+ port number */ +#define SFP_MAX_PORT_NUM 2 +#define SFP_MIN_PORT_NUM 1 + + +/* CPLD registers */ +#define CPLD_BOARD_TYPE_REG 0x0 +#define CPLD_EXT_BOARD_TYPE_REG 0x7 +#define CPLD_VERSION_REG 0x1 +#define CPLD_ID_REG 0x2 +#define CPLD_QSFP_PORT_STATUS_BASE_REG 0x20 +#define CPLD_QSFP_PORT_CONFIG_BASE_REG 0x30 +#define CPLD_QSFP_PORT_INTERRUPT_REG 0x40 +#define CPLD_SFP_PORT_STATUS_REG 0x2F +#define CPLD_SFP_PORT_CONFIG_REG 0x3F +#define CPLD_QSFP_PORT_INTERRUPT_REG 0x40 +#define CPLD_10GMUX_CONFIG_REG 0x41 +#define CPLD_BMC_STATUS_REG 0x42 +#define CPLD_BMC_WATCHDOG_REG 0x43 +#define CPLD_USB_STATUS_REG 0x44 +#define CPLD_REST_CONTROL_REG 0x4A + + +/* bit definition for register value */ +enum CPLD_QSFP_PORT_STATUS_BITS { + CPLD_QSFP_PORT_STATUS_INT_BIT, + CPLD_QSFP_PORT_STATUS_ABS_BIT, +}; +enum CPLD_QSFP_PORT_CONFIG_BITS { + CPLD_QSFP_PORT_CONFIG_RESET_BIT, + CPLD_QSFP_PORT_CONFIG_RESERVE_BIT, + CPLD_QSFP_PORT_CONFIG_LPMODE_BIT, +}; +enum CPLD_SFP_PORT_STATUS_BITS { + CPLD_SFP_PORT_STATUS_PRESENT_BIT, + CPLD_SFP_PORT_STATUS_TXFAULT_BIT, + CPLD_SFP_PORT_STATUS_RXLOS_BIT, +}; +enum CPLD_SFP_PORT_CONFIG_BITS { + CPLD_SFP_PORT_CONFIG_TXDIS_BIT, + CPLD_SFP_PORT_CONFIG_RS_BIT, + CPLD_SFP_PORT_CONFIG_TS_BIT, +}; +enum CPLD_10GMUX_CONFIG_BITS { + CPLD_10GMUX_CONFIG_ENSMB_BIT, + CPLD_10GMUX_CONFIG_ENINPUT_BIT, + CPLD_10GMUX_CONFIG_SEL1_BIT, + CPLD_10GMUX_CONFIG_SEL0_BIT, +}; +enum CPLD_BMC_WATCHDOG_BITS { + CPLD_10GMUX_CONFIG_ENTIMER_BIT, + CPLD_10GMUX_CONFIG_TIMEOUT_BIT, +}; +enum CPLD_RESET_CONTROL_BITS { + CPLD_RESET_CONTROL_SWRST_BIT, + CPLD_RESET_CONTROL_CP2104RST_BIT, + CPLD_RESET_CONTROL_82P33814RST_BIT, + CPLD_RESET_CONTROL_BMCRST_BIT, +}; + +/* bit field structure for register value */ +struct cpld_reg_board_type_t { + u8 build_rev:2; + u8 hw_rev:2; + u8 board_id:4; +}; + +struct cpld_reg_version_t { + u8 revision:6; + u8 release:1; + u8 reserve:1; +}; + +struct cpld_reg_id_t { + u8 id:3; + u8 release:5; +}; + +/* common manipulation */ +#define INVALID(i, min, max) ((i < min) || (i > max) ? 1u : 0u) +#define READ_BIT(val, bit) ((0u == (val & (1<bf_name) +#define READ_BF_1(bf_struct, val, bf_name, bf_value) \ + bf_struct bf; \ + bf.data = val; \ + bf_value = bf.bf_name +#define BOARD_TYPE_BUILD_REV_GET(val, res) \ + READ_BF(cpld_reg_board_type_t, val, build_rev, res) +#define BOARD_TYPE_HW_REV_GET(val, res) \ + READ_BF(cpld_reg_board_type_t, val, hw_rev, res) +#define BOARD_TYPE_BOARD_ID_GET(val, res) \ + READ_BF(cpld_reg_board_type_t, val, board_id, res) +#define CPLD_VERSION_REV_GET(val, res) \ + READ_BF(cpld_reg_version_t, val, revision, res) +#define CPLD_VERSION_REL_GET(val, res) \ + READ_BF(cpld_reg_version_t, val, release, res) +#define CPLD_ID_ID_GET(val, res) \ + READ_BF(cpld_reg_id_t, val, id, res) +#define CPLD_ID_REL_GET(val, res) \ + READ_BF(cpld_reg_id_t, val, release, res) +/* QSFP/SFP registers manipulation */ +#define QSFP_TO_CPLD_IDX(qsfp_port, cpld_index, cpld_port) \ +{ \ + if (QSFP_MIN_PORT_NUM <= qsfp_port && qsfp_port <= CPLD_1_PORT_NUM) { \ + cpld_index = cpld1; \ + cpld_port = qsfp_port - 1; \ + } else if (CPLD_1_PORT_NUM < qsfp_port \ + && qsfp_port <= QSFP_MAX_PORT_NUM) { \ + cpld_index = cpld2 + (qsfp_port - 1 - CPLD_1_PORT_NUM) \ + / CPLD_2_PORT_NUM; \ + cpld_port = (qsfp_port - 1 - CPLD_1_PORT_NUM) % \ + CPLD_2_PORT_NUM; \ + } else { \ + cpld_index = 0; \ + cpld_port = 0; \ + } \ +} +#define SFP_TO_CPLD_IDX(sfp_port, cpld_index) \ + (cpld_index = sfp_port - SFP_MIN_PORT_NUM) +#define QSFP_PORT_STATUS_REG(cpld_port) \ + (CPLD_QSFP_PORT_STATUS_BASE_REG + cpld_port) +#define QSFP_PORT_CONFIG_REG(cpld_port) \ + (CPLD_QSFP_PORT_CONFIG_BASE_REG + cpld_port) +#define QSFP_PORT_INT_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_QSFP_PORT_STATUS_INT_BIT) +#define QSFP_PORT_ABS_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_QSFP_PORT_STATUS_ABS_BIT) +#define QSFP_PORT_RESET_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_RESET_BIT) +#define QSFP_PORT_LPMODE_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_LPMODE_BIT) +#define QSFP_PORT_RESET_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_RESET_BIT) +#define QSFP_PORT_RESET_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_RESET_BIT) +#define QSFP_PORT_LPMODE_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_LPMODE_BIT) +#define QSFP_PORT_LPMODE_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_QSFP_PORT_CONFIG_LPMODE_BIT) +#define SFP_PORT_PRESENT_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_STATUS_PRESENT_BIT) + +#define SFP_PORT_TXFAULT_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_STATUS_TXFAULT_BIT) +#define SFP_PORT_RXLOS_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_STATUS_RXLOS_BIT) +#define SFP_PORT_TXDIS_BIT_GET(port_status_value) \ + READ_BIT(port_status_value, CPLD_SFP_PORT_CONFIG_TXDIS_BIT) +#define SFP_PORT_RS_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_RS_BIT) +#define SFP_PORT_TS_BIT_GET(port_config_value) \ + READ_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TS_BIT) +#define SFP_PORT_TXDIS_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TXDIS_BIT) +#define SFP_PORT_TXDIS_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TXDIS_BIT) +#define SFP_PORT_RS_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_RS_BIT) +#define SFP_PORT_RS_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_RS_BIT) +#define SFP_PORT_TS_BIT_SET(port_config_value) \ + SET_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TS_BIT) +#define SFP_PORT_TS_BIT_CLEAR(port_config_value) \ + CLEAR_BIT(port_config_value, CPLD_SFP_PORT_CONFIG_TS_BIT) + +/* CPLD access functions */ +extern int ingrasys_i2c_cpld_get_qsfp_port_status_val(u8 port_num); +extern int ingrasys_i2c_cpld_get_qsfp_port_config_val(u8 port_num); +extern int ingrasys_i2c_cpld_set_qsfp_port_config_val(u8 port_num, u8 reg_val); +extern int ingrasys_i2c_cpld_get_sfp_port_status_val(u8 port_num); +extern int ingrasys_i2c_cpld_get_sfp_port_config_val(u8 port_num); +extern int ingrasys_i2c_cpld_set_sfp_port_config_val(u8 port_num, u8 reg_val); +extern u8 fp_port_to_phy_port(u8 fp_port); +#endif + diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/ingrasys_s9230_64x_platform.h b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/ingrasys_s9230_64x_platform.h new file mode 100644 index 000000000000..a14853fdae9c --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/ingrasys_s9230_64x_platform.h @@ -0,0 +1,148 @@ +#ifndef _S9230_64X_PLATFORM_H +#define _S9230_64X_PLATFORM_H + +#include + +// remove debug before release +#define DEBUG + +enum bus_order { + I2C_BUS_MAIN, + MUX_9548_0_CH0, + MUX_9548_0_CH1, + MUX_9548_0_CH2, + MUX_9548_0_CH3, + MUX_9548_0_CH4, + MUX_9548_0_CH5, + MUX_9548_0_CH6, + MUX_9548_0_CH7, + MUX_9548_1_CH0, + MUX_9548_1_CH1, + MUX_9548_1_CH2, + MUX_9548_1_CH3, + MUX_9548_1_CH4, + MUX_9548_1_CH5, + MUX_9548_1_CH6, + MUX_9548_1_CH7, + MUX_9546_0_CH0, + MUX_9546_0_CH1, + MUX_9546_0_CH2, + MUX_9546_0_CH3, + MUX_9546_1_CH0, + MUX_9546_1_CH1, + MUX_9546_1_CH2, + MUX_9546_1_CH3, + MUX_9548_2_CH0, + MUX_9548_2_CH1, + MUX_9548_2_CH2, + MUX_9548_2_CH3, + MUX_9548_2_CH4, + MUX_9548_2_CH5, + MUX_9548_2_CH6, + MUX_9548_2_CH7, + MUX_9548_3_CH0, + MUX_9548_3_CH1, + MUX_9548_3_CH2, + MUX_9548_3_CH3, + MUX_9548_3_CH4, + MUX_9548_3_CH5, + MUX_9548_3_CH6, + MUX_9548_3_CH7, + MUX_9548_4_CH0, + MUX_9548_4_CH1, + MUX_9548_4_CH2, + MUX_9548_4_CH3, + MUX_9548_4_CH4, + MUX_9548_4_CH5, + MUX_9548_4_CH6, + MUX_9548_4_CH7, + MUX_9548_5_CH0, + MUX_9548_5_CH1, + MUX_9548_5_CH2, + MUX_9548_5_CH3, + MUX_9548_5_CH4, + MUX_9548_5_CH5, + MUX_9548_5_CH6, + MUX_9548_5_CH7, + MUX_9548_6_CH0, + MUX_9548_6_CH1, + MUX_9548_6_CH2, + MUX_9548_6_CH3, + MUX_9548_6_CH4, + MUX_9548_6_CH5, + MUX_9548_6_CH6, + MUX_9548_6_CH7, + MUX_9548_7_CH0, + MUX_9548_7_CH1, + MUX_9548_7_CH2, + MUX_9548_7_CH3, + MUX_9548_7_CH4, + MUX_9548_7_CH5, + MUX_9548_7_CH6, + MUX_9548_7_CH7, + MUX_9548_8_CH0, + MUX_9548_8_CH1, + MUX_9548_8_CH2, + MUX_9548_8_CH3, + MUX_9548_8_CH4, + MUX_9548_8_CH5, + MUX_9548_8_CH6, + MUX_9548_8_CH7, + MUX_9548_9_CH0, + MUX_9548_9_CH1, + MUX_9548_9_CH2, + MUX_9548_9_CH3, + MUX_9548_9_CH4, + MUX_9548_9_CH5, + MUX_9548_9_CH6, + MUX_9548_9_CH7, + MUX_9548_10_CH0, + MUX_9548_10_CH1, + MUX_9548_10_CH2, + MUX_9548_10_CH3, + MUX_9548_10_CH4, + MUX_9548_10_CH5, + MUX_9548_10_CH6, + MUX_9548_10_CH7, +}; + +#define I2C_ADDR_MUX_9555_0 (0x20) +#define I2C_ADDR_MUX_9555_1 (0x24) +#define I2C_ADDR_MUX_9555_2 (0x25) +#define I2C_ADDR_MUX_9555_3 (0x26) +#define I2C_ADDR_MUX_9539_0 (0x76) +#define I2C_ADDR_MUX_9539_1 (0x76) +#define I2C_BUS_FAN_STATUS (I2C_BUS_MAIN) +#define I2C_BUS_SYS_LED (MUX_9548_1_CH1) + +#define NUM_OF_I2C_MUX (11) +#define NUM_OF_CPLD (5) +#define NUM_OF_QSFP_PORT (64) +#define NUM_OF_SFP_PORT (2) +#define QSFP_EEPROM_I2C_ADDR (0x50) + +enum gpio_reg { + REG_PORT0_IN, + REG_PORT1_IN, + REG_PORT0_OUT, + REG_PORT1_OUT, + REG_PORT0_POL, + REG_PORT1_POL, + REG_PORT0_DIR, + REG_PORT1_DIR, +}; + +struct ing_i2c_board_info { + int ch; + int size; + struct i2c_board_info *board_info; +}; + +struct i2c_init_data { + __u16 ch; + __u16 addr; + __u8 reg; + __u8 value; +}; + +#endif diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/ingrasys_s9230_64x_psu.c b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/ingrasys_s9230_64x_psu.c new file mode 100644 index 000000000000..a163a7cafcd2 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/modules/ingrasys_s9230_64x_psu.c @@ -0,0 +1,407 @@ +/* + * S9230-64x PSU driver + * + * Copyright (C) 2017 Ingrasys, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ingrasys_s9230_64x_platform.h" + +static ssize_t show_psu_eeprom(struct device *dev, + struct device_attribute *da, + char *buf); +static struct s9230_psu_data *s9230_psu_update_status(struct device *dev); +static struct s9230_psu_data *s9230_psu_update_eeprom(struct device *dev); +static int s9230_psu_read_block(struct i2c_client *client, + u8 command, + u8 *data, + int data_len); + + +#define DRIVER_NAME "psu" + +// Addresses scanned +static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; + +/* PSU EEPROM SIZE */ +#define EEPROM_SZ 256 +#define READ_EEPROM 1 +#define NREAD_EEPROM 0 + +static struct i2c_client pca9555_client; + +/* pca9555 gpio pin mapping */ +#define PSU2_PWROK 0 +#define PSU2_PRSNT_L 1 +#define PSU2_PWRON_L 2 +#define PSU1_PWROK 3 +#define PSU1_PRSNT_L 4 +#define PSU1_PWRON_L 5 +#define TMP_75_INT_L 6 + +/* Driver Private Data */ +struct s9230_psu_data { + struct device *hwmon_dev; + struct mutex lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + s32 status; /* IO expander value */ + char eeprom[EEPROM_SZ]; /* psu eeprom data */ + char psuABS; /* PSU absent */ + char psuPG; /* PSU power good */ +}; + +enum s9230_psu_sysfs_attributes { + PSU_POWER_GOOD, + PSU_ABSENT, + PSU_EEPROM +}; + +enum psu_index +{ + s9230_psu1, + s9230_psu2 +}; + +/* + * display power good attribute + */ +static ssize_t +show_psu_pg(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct s9230_psu_data *data = s9230_psu_update_status(dev); + unsigned int value; + + mutex_lock(&data->lock); + value = data->psuPG; + mutex_unlock(&data->lock); + + return sprintf(buf, "%d\n", value); +} + +/* + * display power absent attribute + */ +static ssize_t +show_psu_abs(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct s9230_psu_data *data = s9230_psu_update_status(dev); + unsigned int value; + + mutex_lock(&data->lock); + value = data->psuABS; + mutex_unlock(&data->lock); + + return sprintf(buf, "%d\n", value); +} + + +/* + * sysfs attributes for psu + */ +static SENSOR_DEVICE_ATTR(psu_pg, S_IRUGO, show_psu_pg, NULL, PSU_POWER_GOOD); +static SENSOR_DEVICE_ATTR(psu_abs, S_IRUGO, show_psu_abs, NULL, PSU_ABSENT); +static SENSOR_DEVICE_ATTR(psu_eeprom, S_IRUGO, show_psu_eeprom, NULL, PSU_EEPROM); + +static struct attribute *s9230_psu_attributes[] = { + &sensor_dev_attr_psu_pg.dev_attr.attr, + &sensor_dev_attr_psu_abs.dev_attr.attr, + &sensor_dev_attr_psu_eeprom.dev_attr.attr, + NULL +}; + +/* + * display psu eeprom content + */ +static ssize_t +show_psu_eeprom(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct s9230_psu_data *data = s9230_psu_update_eeprom(dev); + + memcpy(buf, (char *)data->eeprom, EEPROM_SZ); + return EEPROM_SZ; +} + +static const struct attribute_group s9230_psu_group = { + .attrs = s9230_psu_attributes, +}; + +/* + * check gpio expander is accessible + */ +static int +pca9555_detect(struct i2c_client *client) +{ + if (i2c_smbus_read_byte_data(client, REG_PORT0_DIR) < 0) { + return -ENODEV; + } + + return 0; +} + +/* + * client address init + */ +static void +i2c_devices_client_address_init(struct i2c_client *client) +{ + pca9555_client = *client; + pca9555_client.addr = 0x25; +} + +static int +s9230_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct s9230_psu_data *data; + int status, err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct s9230_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct s9230_psu_data)); + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->lock); + + i2c_devices_client_address_init(client); + + err = pca9555_detect(&pca9555_client); + if (err) { + return err; + } + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &s9230_psu_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &s9230_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int +s9230_psu_remove(struct i2c_client *client) +{ + struct s9230_psu_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &s9230_psu_group); + kfree(data); + + return 0; +} + + +/* + * psu eeprom read utility + */ +static int +s9230_psu_read_block(struct i2c_client *client, + u8 command, + u8 *data, + int data_len) +{ + int i=0, ret=0; + int blk_max = 32; //max block read size + + /* read eeprom, 32 * 8 = 256 bytes */ + for (i=0; i < EEPROM_SZ/blk_max; i++) { + ret = i2c_smbus_read_i2c_block_data(client, (i*blk_max), blk_max, + data + (i*blk_max)); + if (ret < 0) { + return ret; + } + } + return ret; +} + +/* + * update eeprom content + */ +static struct s9230_psu_data +*s9230_psu_update_eeprom(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s9230_psu_data *data = i2c_get_clientdata(client); + s32 status = 0; + int psu_pwrok = 0; + int psu_prsnt_l = 0; + + mutex_lock(&data->lock); + + if (time_after(jiffies, data->last_updated + 300 * HZ) + || !data->valid) { + + /* Read psu status */ + + status = i2c_smbus_read_word_data(&(pca9555_client), REG_PORT0_IN); + data->status = status; + + /*read psu status from io expander*/ + + if (data->index == s9230_psu1) { + psu_pwrok = PSU1_PWROK; + psu_prsnt_l = PSU1_PRSNT_L; + } else { + psu_pwrok = PSU2_PWROK; + psu_prsnt_l = PSU2_PRSNT_L; + } + data->psuPG = (status >> psu_pwrok) & 0x1; + data->psuABS = (status >> psu_prsnt_l) & 0x1; + + /* Read eeprom */ + if (!data->psuABS) { + //clear local eeprom data + memset(data->eeprom, 0, EEPROM_SZ); + + //read eeprom + status = s9230_psu_read_block(client, 0, data->eeprom, + ARRAY_SIZE(data->eeprom)); + + if (status < 0) { + memset(data->eeprom, 0, EEPROM_SZ); + dev_err(&client->dev, "Read eeprom failed, status=(%d)\n", status); + } else { + data->valid = 1; + } + } else { + memset(data->eeprom, 0, EEPROM_SZ); + } + data->last_updated = jiffies; + } + + mutex_unlock(&data->lock); + + return data; +} + +/* + * update psu status + */ +static struct s9230_psu_data +*s9230_psu_update_status(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s9230_psu_data *data = i2c_get_clientdata(client); + s32 status = 0; + int psu_pwrok = 0; + int psu_prsnt_l = 0; + + mutex_lock(&data->lock); + + /* Read psu status */ + + status = i2c_smbus_read_word_data(&(pca9555_client), REG_PORT0_IN); + data->status = status; + + /*read psu status from io expander*/ + + if (data->index == s9230_psu1) { + psu_pwrok = PSU1_PWROK; + psu_prsnt_l = PSU1_PRSNT_L; + } else { + psu_pwrok = PSU2_PWROK; + psu_prsnt_l = PSU2_PRSNT_L; + } + data->psuPG = (status >> psu_pwrok) & 0x1; + data->psuABS = (status >> psu_prsnt_l) & 0x1; + + mutex_unlock(&data->lock); + + return data; +} + +static const struct i2c_device_id s9230_psu_id[] = { + { "psu1", s9230_psu1 }, + { "psu2", s9230_psu2 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, s9230_psu_id); + +static struct i2c_driver s9230_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRIVER_NAME, + }, + .probe = s9230_psu_probe, + .remove = s9230_psu_remove, + .id_table = s9230_psu_id, + .address_list = normal_i2c, +}; + +static int __init s9230_psu_init(void) +{ + return i2c_add_driver(&s9230_psu_driver); +} + +static void __exit s9230_psu_exit(void) +{ + i2c_del_driver(&s9230_psu_driver); +} + +module_init(s9230_psu_init); +module_exit(s9230_psu_exit); + +MODULE_AUTHOR("Jason Tsai "); +MODULE_DESCRIPTION("S9230-64X psu driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/service/qsfp-monitor.service b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/service/qsfp-monitor.service new file mode 100644 index 000000000000..cf4a6a8ad41e --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/service/qsfp-monitor.service @@ -0,0 +1,15 @@ +[Unit] +Description= This QSFP Monitor service is to setup QSFP SI. +Requires=s9230-64x-monitor.service +After=s9230-64x-monitor.service + +[Service] +ExecStart=/usr/sbin/qsfp_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/service/s9230-64x-monitor.service b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/service/s9230-64x-monitor.service new file mode 100644 index 000000000000..5caf968e659a --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/service/s9230-64x-monitor.service @@ -0,0 +1,19 @@ +[Unit] +Description= This Platform Monitor service is to initialize platform and monitor platform. +Before=platform-monitor.service +After=sysinit.target +Wants=fancontrol.service +Wants=qsfp-monitor.service +DefaultDependencies=no + +[Service] +ExecStartPre=/usr/sbin/i2c_utils.sh i2c_init +ExecStart=/usr/sbin/s9230_64x_monitor.sh +KillSignal=SIGKILL +SuccessExitStatus=SIGKILL + +# Resource Limitations +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/utils/i2c_utils.sh b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/utils/i2c_utils.sh new file mode 100755 index 000000000000..32506ce20863 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/utils/i2c_utils.sh @@ -0,0 +1,1850 @@ +#!/bin/bash + +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +# trun on for more debug output +#DEBUG="on" + +VERSION="1.0.0" +TRUE=200 +FALSE=404 + +EXEC_FUNC=${1} +COLOR_LED=${2} +QSFP_PORT=${2} +SFP_PORT=${2} +QSFP_ACTION=${2} +SFP_ACTION=${2} +MB_EEPROM_ACTION=${2} +TARGET_10G_MUX=${2} +ONOFF_LED=${3} +FAN_TRAY=${4} + +############################################################ +# Distributor ID: Debian +# Description: Debian GNU/Linux 8.6 (jessie) +# Release: 8.6 +# Codename: jessie +# Linux debian 3.16.0-4-amd64 #1 +# SMP Debian 3.16.36-1+deb8u1 (2016-09-03) x86_64 GNU/Linux +############################################################ + +# Color Definition +COLOR_TITLE="\e[1;32m" ### Green ### +COLOR_WARNING="\e[1;33m" ### Yellow ### +COLOR_ERROR="\e[1;31m" ### Red ### +COLOR_END="\e[0m" ### END ### + +NUM_I801_DEVICE=0 + +# PCA9548#0 0x70 +NUM_MUX_9548_0_CH0=$(( ${NUM_I801_DEVICE} + 1 )) # CPLD1 +NUM_MUX_9548_0_CH1=$(( ${NUM_I801_DEVICE} + 2 )) # CPLD2 +NUM_MUX_9548_0_CH2=$(( ${NUM_I801_DEVICE} + 3 )) # CPLD3 +NUM_MUX_9548_0_CH3=$(( ${NUM_I801_DEVICE} + 4 )) # CPLD4 +NUM_MUX_9548_0_CH4=$(( ${NUM_I801_DEVICE} + 5 )) # CPLD5 +NUM_MUX_9548_0_CH5=$(( ${NUM_I801_DEVICE} + 6 )) # LM75_1 LM75_2 +NUM_MUX_9548_0_CH6=$(( ${NUM_I801_DEVICE} + 7 )) # LM75_3 LM75_4 + +# PCA9548#1 0x73 +NUM_MUX_9548_1_CH0=$(( ${NUM_I801_DEVICE} + 9 )) # UCD9090 +NUM_MUX_9548_1_CH1=$(( ${NUM_I801_DEVICE} + 10 )) # PCA9539 0x76 for FP LED & HW ID +NUM_MUX_9548_1_CH2=$(( ${NUM_I801_DEVICE} + 11 )) # PCA9539 0x76 for SYS control +NUM_MUX_9548_1_CH4=$(( ${NUM_I801_DEVICE} + 13 )) # 10G Mux +NUM_MUX_9548_1_CH7=$(( ${NUM_I801_DEVICE} + 16 )) # HWM & TMP75 on BMC + +# PCA9546#0 0X72 +NUM_MUX_9546_0_CH0=$(( ${NUM_I801_DEVICE} + 17 )) # PSU1 0x58 +NUM_MUX_9546_0_CH1=$(( ${NUM_I801_DEVICE} + 18 )) # PSU2 0x58 +NUM_MUX_9546_0_CH2=$(( ${NUM_I801_DEVICE} + 19 )) # PCA9548#2 0x71 +NUM_MUX_9546_0_CH3=$(( ${NUM_I801_DEVICE} + 20 )) # PCA9546#1 0x71 + +# PCA9546#1 0X71 +NUM_MUX_9546_1_CH0=$(( ${NUM_I801_DEVICE} + 21 )) # SFP0 EEPROM 0x50 +NUM_MUX_9546_1_CH1=$(( ${NUM_I801_DEVICE} + 22 )) # SFP1 EEPROM 0x50 + +# PCA9548#2 0X71 +NUM_MUX_9548_2_CH0=$(( ${NUM_I801_DEVICE} + 25 )) # PCA9548#3 0x74 +NUM_MUX_9548_2_CH1=$(( ${NUM_I801_DEVICE} + 26 )) # PCA9548#4 0x74 +NUM_MUX_9548_2_CH2=$(( ${NUM_I801_DEVICE} + 27 )) # PCA9548#5 0x74 +NUM_MUX_9548_2_CH3=$(( ${NUM_I801_DEVICE} + 28 )) # PCA9548#6 0x74 +NUM_MUX_9548_2_CH4=$(( ${NUM_I801_DEVICE} + 29 )) # PCA9548#7 0x74 +NUM_MUX_9548_2_CH5=$(( ${NUM_I801_DEVICE} + 30 )) # PCA9548#8 0x74 +NUM_MUX_9548_2_CH6=$(( ${NUM_I801_DEVICE} + 31 )) # PCA9548#9 0x74 +NUM_MUX_9548_2_CH7=$(( ${NUM_I801_DEVICE} + 32 )) # PCA9548#10 0x74 + +# PCA9548#3~10 0X74 +NUM_MUX_9548_3_CH0=$(( ${NUM_I801_DEVICE} + 33 )) # QSFP 0 EEPROM +NUM_MUX_9548_4_CH0=$(( ${NUM_I801_DEVICE} + 41 )) # QSFP 8 EEPROM +NUM_MUX_9548_5_CH0=$(( ${NUM_I801_DEVICE} + 49 )) # QSFP 16 EEPROM +NUM_MUX_9548_6_CH0=$(( ${NUM_I801_DEVICE} + 57 )) # QSFP 24 EEPROM +NUM_MUX_9548_7_CH0=$(( ${NUM_I801_DEVICE} + 65 )) # QSFP 32 EEPROM +NUM_MUX_9548_8_CH0=$(( ${NUM_I801_DEVICE} + 73 )) # QSFP 40 EEPROM +NUM_MUX_9548_9_CH0=$(( ${NUM_I801_DEVICE} + 81 )) # QSFP 48 EEPROM +NUM_MUX_9548_10_CH0=$(( ${NUM_I801_DEVICE} + 89 )) # QSFP 56 EEPROM + +# MUX Alias +I2C_BUS_MAIN=${NUM_I801_DEVICE} +I2C_BUS_HWM=${NUM_MUX_9548_1_CH7} +I2C_BUS_FAN_STATUS=${I2C_BUS_MAIN} +I2C_BUS_SYS_LED=${NUM_MUX_9548_1_CH1} +I2C_BUS_HW_ID=${NUM_MUX_9548_1_CH1} +I2C_BUS_BMC_HW_ID=${I2C_BUS_MAIN} +I2C_BUS_PSU_STAT=${I2C_BUS_MAIN} +I2C_BUS_FANTRAY_LED=${I2C_BUS_MAIN} +I2C_BUS_MB_EEPROM=${I2C_BUS_MAIN} +I2C_BUS_CB_EEPROM=${I2C_BUS_MAIN} +I2C_BUS_PSU1_EEPROM=${NUM_MUX_9546_0_CH1} +I2C_BUS_PSU2_EEPROM=${NUM_MUX_9546_0_CH0} +I2C_BUS_CPLD1=${NUM_MUX_9548_0_CH0} +I2C_BUS_CPLD2=${NUM_MUX_9548_0_CH1} +I2C_BUS_CPLD3=${NUM_MUX_9548_0_CH2} +I2C_BUS_CPLD4=${NUM_MUX_9548_0_CH3} +I2C_BUS_CPLD5=${NUM_MUX_9548_0_CH4} +I2C_BUS_10GMUX=${NUM_MUX_9548_1_CH4} + +# I2C BUS path +PATH_SYS_I2C_DEVICES="/sys/bus/i2c/devices" +PATH_HWMON_ROOT_DEVICES="/sys/class/hwmon" +# TODO: need to verify HWM deivce path after board ready +PATH_HWMON_W83795_DEVICE="${PATH_HWMON_ROOT_DEVICES}/hwmon7" +PATH_I801_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_I801_DEVICE}" +PATH_MUX_9548_0_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CH0}" +PATH_MUX_9548_0_CH1="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CH1}" +PATH_MUX_9548_0_CH2="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CH2}" +PATH_MUX_9548_0_CH3="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CH3}" +PATH_MUX_9548_0_CH4="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CH4}" +PATH_MUX_9548_0_CH5="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CH5}" +PATH_MUX_9548_0_CH6="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_0_CH6}" +PATH_MUX_9548_1_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CH0}" +PATH_MUX_9548_1_CH1="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CH1}" +PATH_MUX_9548_1_CH2="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CH2}" +PATH_MUX_9548_1_CH7="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_1_CH7}" +PATH_MUX_9546_0_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9546_0_CH0}" +PATH_MUX_9546_0_CH1="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9546_0_CH1}" +PATH_MUX_9546_0_CH2="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9546_0_CH2}" +PATH_MUX_9546_0_CH3="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9546_0_CH3}" +PATH_MUX_9546_1_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9546_1_CH0}" +PATH_MUX_9546_1_CH1="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9546_1_CH1}" +PATH_MUX_9548_2_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH0}" +PATH_MUX_9548_2_CH1="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH1}" +PATH_MUX_9548_2_CH2="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH2}" +PATH_MUX_9548_2_CH3="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH3}" +PATH_MUX_9548_2_CH4="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH4}" +PATH_MUX_9548_2_CH5="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH5}" +PATH_MUX_9548_2_CH6="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH6}" +PATH_MUX_9548_2_CH7="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_2_CH7}" +PATH_MUX_9548_3_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_3_CH0}" +PATH_MUX_9548_4_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_4_CH0}" +PATH_MUX_9548_5_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_5_CH0}" +PATH_MUX_9548_6_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_6_CH0}" +PATH_MUX_9548_7_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_7_CH0}" +PATH_MUX_9548_8_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_8_CH0}" +PATH_MUX_9548_9_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_9_CH0}" +PATH_MUX_9548_10_CH0="${PATH_SYS_I2C_DEVICES}/i2c-${NUM_MUX_9548_10_CH0}" +PATH_CPLD1_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_CPLD1}" +PATH_CPLD2_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_CPLD2}" +PATH_CPLD3_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_CPLD3}" +PATH_CPLD4_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_CPLD4}" +PATH_CPLD5_DEVICE="${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_CPLD5}" +PATH_10GMUX=${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_10GMUX} + + +# I2C Address +### I2C MUX +I2C_ADDR_MUX_9548_0=0x70 +I2C_ADDR_MUX_9548_1=0x73 +I2C_ADDR_MUX_9546_0=0x72 +I2C_ADDR_MUX_9546_1=0x71 +I2C_ADDR_MUX_9548_2=0x71 +I2C_ADDR_MUX_9548_3=0x74 +I2C_ADDR_MUX_9548_4=0x74 +I2C_ADDR_MUX_9548_5=0x74 +I2C_ADDR_MUX_9548_6=0x74 +I2C_ADDR_MUX_9548_7=0x74 +I2C_ADDR_MUX_9548_8=0x74 +I2C_ADDR_MUX_9548_9=0x74 +I2C_ADDR_MUX_9548_10=0x74 + +### GPIO Expander +I2C_ADDR_MUX_9539_0=0x76 # LED & HW ID +I2C_ADDR_MUX_9539_1=0x76 # SYS config +I2C_ADDR_MUX_9539_2=0x77 # on CPU board, STATUS and ERR from CPLD +I2C_ADDR_MUX_9555_0=0x20 # on FAN board, fan status and led config +I2C_ADDR_MUX_9555_1=0x24 # on BMC board, INT and HW ID +I2C_ADDR_MUX_9555_2=0x25 # on BMC board, PSU status +I2C_ADDR_MUX_9555_3=0x26 # on BMC board, RST and SEL +I2C_ADDR_MUX_9555_3=0x26 # on BMC board, RST and SEL + + +### peripheral +I2C_ADDR_MB_EEPROM=0x55 # on main board +I2C_ADDR_CB_EEPROM=0x51 # on cpu board +I2C_ADDR_UCD9090=0x34 +I2C_ADDR_W83795=0x2F +I2C_ADDR_PSU1_EEPROM=0x50 +I2C_ADDR_PSU2_EEPROM=0x50 +I2C_ADDR_LM75_1=0x4D # Rear Panel +I2C_ADDR_LM75_2=0x4E # Rear MAC +I2C_ADDR_LM75_3=0x4D # Front Panel +I2C_ADDR_LM75_4=0x4E # Front MAC +I2C_ADDR_TMP75_CB=0x4F # on cpu board +I2C_ADDR_TMP75_BB=0x4A # on bmc board +I2C_ADDR_QSFP_EEPROM=0x50 +I2C_ADDR_SFP_EEPROM=0x50 +I2C_ADDR_CPLD=0x33 +I2C_ADDR_10GMUX=0x67 + +#sysfs +PATH_SYSFS_PSU1="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_PSU1_EEPROM}-$(printf "%04x" $I2C_ADDR_PSU1_EEPROM)" +PATH_SYSFS_PSU2="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_PSU2_EEPROM}-$(printf "%04x" $I2C_ADDR_PSU2_EEPROM)" + +#ACTIVE LOW enable flag +ACTIVE_LOW_EN=1 +ACTIVE_HIGH_EN=0 +#GPIO Direction In/Out +DIR_IN=in +DIR_OUT=out + +#Power Supply Status +PSU_DC_ON=1 +PSU_DC_OFF=0 +PSU_EXIST=1 +PSU_NOT_EXIST=0 + +# IO expander register +# direction +REG_PORT0_DIR=6 +REG_PORT1_DIR=7 +# polarity +REG_PORT0_POL=4 +REG_PORT1_POL=5 +# output +REG_PORT0_OUT=2 +REG_PORT1_OUT=3 +# input +REG_PORT0_IN=0 +REG_PORT1_IN=1 + +# qsfp port number range +MIN_QSFP_PORT_NUM=1 +MAX_QSFP_PORT_NUM=64 + +# sfp+ port number range +MIN_SFP_PORT_NUM=1 +MAX_SFP_PORT_NUM=2 + +# CPLD access +# port status +CPLD_QSFP_STATUS_KEY=cpld_qsfp_port_status +CPLD_SFP_STATUS_KEY=cpld_sfp_port_status +# bit define +CPLD_QSFP_STATUS_ABS_BIT=1 +CPLD_SFP_STATUS_PRES_BIT=0 + +# Help usage function +function _help { + echo "=========================================================" + echo "# Description: Help Function" + echo "=========================================================" + echo "----------------------------------------------------" + echo "EX : ${0} help" + echo " : ${0} i2c_init" + echo " : ${0} i2c_deinit" + echo " : ${0} i2c_fan_speed_init" + echo " : ${0} i2c_io_exp_init" + echo " : ${0} i2c_led_test" + echo " : ${0} i2c_psu_eeprom_get" + echo " : ${0} i2c_mb_eeprom_get" + echo " : ${0} i2c_cb_eeprom_get" + #echo " : ${0} i2c_eeprom_sync" + echo " : ${0} i2c_qsfp_eeprom_get [${MIN_QSFP_PORT_NUM}-${MAX_QSFP_PORT_NUM}]" + echo " : ${0} i2c_sfp_eeprom_get [${MIN_SFP_PORT_NUM}-${MAX_SFP_PORT_NUM}]" + echo " : ${0} i2c_qsfp_eeprom_init new|delete" + echo " : ${0} i2c_sfp_eeprom_init new|delete" + echo " : ${0} i2c_mb_eeprom_init new|delete" + echo " : ${0} i2c_cb_eeprom_init new|delete" + echo " : ${0} i2c_qsfp_status_get [${MIN_QSFP_PORT_NUM}-${MAX_QSFP_PORT_NUM}]" + echo " : ${0} i2c_sfp_status_get [${MIN_SFP_PORT_NUM}-${MAX_SFP_PORT_NUM}]" + echo " : ${0} i2c_qsfp_type_get [${MIN_QSFP_PORT_NUM}-${MAX_QSFP_PORT_NUM}]" + echo " : ${0} i2c_sfp_type_get [${MIN_SFP_PORT_NUM}-${MAX_SFP_PORT_NUM}]" + echo " : ${0} i2c_board_type_get" + echo " : ${0} i2c_bmc_board_type_get" + echo " : ${0} i2c_cpld_version" + echo " : ${0} i2c_psu_status" + echo " : ${0} i2c_led_psu_status_set" + echo " : ${0} i2c_led_fan_status_set" + echo " : ${0} i2c_led_fan_tray_status_set" + echo " : ${0} i2c_test_all" + echo " : ${0} i2c_sys_led green|amber" + echo " : ${0} i2c_fan_led green|amber|off" + echo " : ${0} i2c_psu1_led green|amber|off" + echo " : ${0} i2c_psu2_led green|amber|off" + echo " : ${0} i2c_fan_tray_led green|amber on|off [1-4]" + echo " : ${0} i2c_10g_mux cpu|fp" + echo "----------------------------------------------------" +} + +#Pause function +function _pause { + read -p "$*" +} + +#Retry command function +function _retry { + local i + for i in {1..5}; + do + eval "${*}" && break || echo "retry"; sleep 1; + done +} + +#logical(front panel) to physical (falcon core) port mapping +function _port_logic2phy { + + local logic_port=$1 + local phy_port=0 + + if (( $logic_port >=1 && $logic_port <= 32 )) ; then + phy_port=$(( (logic_port-1)/2*4 + (logic_port-1)%2 + 1)) + elif (( $logic_port >=33 && $logic_port <= 64 )) ; then + phy_port=$(( (((logic_port-1)%32))/2*4 + (logic_port-1)%2 + 3)) + fi + + echo $phy_port +} + +#I2C Init +function _i2c_init { + echo "=========================================================" + echo "# Description: I2C Init" + echo "=========================================================" + + #rmmod i2c_ismt + _util_rmmod i2c_i801 + modprobe i2c_i801 + modprobe i2c_dev + modprobe i2c_mux_pca954x force_deselect_on_exit=1 + + # add MUX PCA9548#0 on I801, assume to be i2c-1~8 + if [ ! -e ${PATH_MUX_9548_0_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_0}' > ${PATH_I801_DEVICE}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_0} already init." + fi + + # add MUX PCA9548#1 on I801, assume to be i2c-9~16 + if [ ! -e ${PATH_MUX_9548_1_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_1}' > ${PATH_I801_DEVICE}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_1} already init." + fi + + # add MUX PCA9546#0 on I801, assume to be i2c-17~20 + if [ ! -e ${PATH_MUX_9546_0_CH0} ]; then + _retry "echo 'pca9546 ${I2C_ADDR_MUX_9546_0}' > ${PATH_I801_DEVICE}/new_device" + else + echo "pca9546 ${I2C_ADDR_MUX_9546_0} already init." + fi + + # add MUX PCA9546#1 on PCA9546#0 CH3, assume to be i2c-21~24 + if [ ! -e ${PATH_MUX_9546_1_CH0} ]; then + _retry "echo 'pca9546 ${I2C_ADDR_MUX_9546_1}' > ${PATH_MUX_9546_0_CH3}/new_device" + else + echo "pca9546 ${I2C_ADDR_MUX_9546_1} already init." + fi + + # add MUX PCA9548#2 on PCA9546#0 CH2, assume to be i2c-25~32 + if [ ! -e ${PATH_MUX_9548_2_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_2}' > ${PATH_MUX_9546_0_CH2}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_2} already init." + fi + + # add MUX PCA9548#3 on PCA9548#2 CH0, assume to be i2c-33~40 + if [ ! -e ${PATH_MUX_9548_3_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_3}' > ${PATH_MUX_9548_2_CH0}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_3} already init." + fi + + # add MUX PCA9548#4 on PCA9548#2 CH1, assume to be i2c-41~48 + if [ ! -e ${PATH_MUX_9548_4_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_4}' > ${PATH_MUX_9548_2_CH1}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_4} already init." + fi + + # add MUX PCA9548#5 on PCA9548#2 CH2, assume to be i2c-49~56 + if [ ! -e ${PATH_MUX_9548_5_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_5}' > ${PATH_MUX_9548_2_CH2}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_5} already init." + fi + + # add MUX PCA9548#6 on PCA9548#2 CH3, assume to be i2c-57~64 + if [ ! -e ${PATH_MUX_9548_6_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_6}' > ${PATH_MUX_9548_2_CH3}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_6} already init." + fi + + # add MUX PCA9548#7 on PCA9548#2 CH4, assume to be i2c-65~72 + if [ ! -e ${PATH_MUX_9548_7_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_7}' > ${PATH_MUX_9548_2_CH4}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_7} already init." + fi + + # add MUX PCA9548#8 on PCA9548#2 CH5, assume to be i2c-73~80 + if [ ! -e ${PATH_MUX_9548_8_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_8}' > ${PATH_MUX_9548_2_CH5}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_8} already init." + fi + + # add MUX PCA9548#9 on PCA9548#2 CH6, assume to be i2c-81~88 + if [ ! -e ${PATH_MUX_9548_9_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_9}' > ${PATH_MUX_9548_2_CH6}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_9} already init." + fi + + # add MUX PCA9548#10 on PCA9548#2 CH7, assume to be i2c-89~96 + if [ ! -e ${PATH_MUX_9548_10_CH0} ]; then + _retry "echo 'pca9548 ${I2C_ADDR_MUX_9548_10}' > ${PATH_MUX_9548_2_CH7}/new_device" + else + echo "pca9548 ${I2C_ADDR_MUX_9548_10} already init." + fi + + _i2c_hwm_init + _util_rmmod eeprom + modprobe eeprom_mb + modprobe gpio-pca953x + _i2c_io_exp_init + _i2c_sensors_init + _i2c_cpld_init + _i2c_psu_init + + _i2c_qsfp_eeprom_init "new" + _i2c_sfp_eeprom_init "new" + _i2c_mb_eeprom_init "new" + _i2c_cb_eeprom_init "new" + _i2c_fan_speed_init + _i2c_led_psu_status_set + _i2c_led_fan_status_set + + # trun on sys led + echo "led_sys setup..." + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_sys_led + + _config_rmem +} + +#I2C Deinit +function _i2c_deinit { + echo "i2c deinit..." + for mod in coretemp jc42 w83795 eeprom eeprom_mb gpio-pca953x i2c_mux_pca954x i2c_i801 ingrasys_s9230_64x_psu; + do + _util_rmmod $mod + done + echo "Done" +} + +function _i2c_cpld_init { + echo "CPLD init..." + _util_rmmod ingrasys_s9230_64x_i2c_cpld + modprobe ingrasys_s9230_64x_i2c_cpld + + # add cpld 1~5 to sysfs + for i in {1..5}; + do + local cpld_bus="I2C_BUS_CPLD${i}" + local cpld_path="PATH_CPLD${i}_DEVICE" + dev_path="${PATH_SYS_I2C_DEVICES}/${!cpld_bus}-$(printf "%04x" ${I2C_ADDR_CPLD})" + if ! [ -L ${dev_path} ]; then + echo "ingrasys_cpld${i} ${I2C_ADDR_CPLD}" > ${!cpld_path}/new_device + else + echo "${dev_path} already exist" + fi + done + + echo "Done" +} + +function _i2c_sensors_init { + echo "SENSORS init..." + local dev_path + # to make sure hwmon index in sysfs as expected, + # need to remove kernel module and then probe them in expected order + # remove all sensors kernel module + _util_rmmod coretemp + _util_rmmod jc42 + _util_rmmod w83795 + # probe coretemp kernel module + modprobe coretemp + # probe hwmon kernel module + modprobe w83795 + # add lm75 to sysfs + ####Main board thermal + ####lm75_1 + dev_path="${PATH_SYS_I2C_DEVICES}/${NUM_MUX_9548_0_CH5}-$(printf "%04x" ${I2C_ADDR_LM75_1})" + if ! [ -L ${dev_path} ]; then + echo "lm75 ${I2C_ADDR_LM75_1}" > ${PATH_MUX_9548_0_CH5}/new_device # hwmon1 + else + echo "${dev_path} already exist" + fi + ####lm75_2 + dev_path="${PATH_SYS_I2C_DEVICES}/${NUM_MUX_9548_0_CH5}-$(printf "%04x" ${I2C_ADDR_LM75_2})" + if ! [ -L ${dev_path} ]; then + echo "lm75 ${I2C_ADDR_LM75_2}" > ${PATH_MUX_9548_0_CH5}/new_device #hwmon2 + else + echo "${dev_path} already exist" + fi + ####lm75_3 + dev_path="${PATH_SYS_I2C_DEVICES}/${NUM_MUX_9548_0_CH6}-$(printf "%04x" ${I2C_ADDR_LM75_3})" + if ! [ -L ${dev_path} ]; then + echo "lm75 ${I2C_ADDR_LM75_1}" > ${PATH_MUX_9548_0_CH6}/new_device # hwmon3 + else + echo "${dev_path} already exist" + fi + ####lm75_4 + dev_path="${PATH_SYS_I2C_DEVICES}/${NUM_MUX_9548_0_CH6}-$(printf "%04x" ${I2C_ADDR_LM75_4})" + if ! [ -L ${dev_path} ]; then + echo "lm75 ${I2C_ADDR_LM75_4}" > ${PATH_MUX_9548_0_CH6}/new_device #hwmon4 + else + echo "${dev_path} already exist" + fi + ####BMC board thermal + dev_path="${PATH_SYS_I2C_DEVICES}/${NUM_MUX_9548_1_CH7}-$(printf "%04x" ${I2C_ADDR_TMP75_BB})" + if ! [ -L ${dev_path} ]; then + echo "tmp75 ${I2C_ADDR_TMP75_BB}" > ${PATH_MUX_9548_1_CH7}/new_device #hwmon5 + else + echo "${dev_path} already exist" + fi + ####CPU board thermal + dev_path="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MAIN}-$(printf "%04x" ${I2C_ADDR_TMP75_CB})" + if ! [ -L ${dev_path} ]; then + echo "tmp75 ${I2C_ADDR_TMP75_CB}" > ${PATH_I801_DEVICE}/new_device #hwmon6 + else + echo "${dev_path} already exist" + fi + # add w83795 to sysfs + dev_path="${PATH_SYS_I2C_DEVICES}/${NUM_MUX_9548_1_CH7}-$(printf "%04x" ${I2C_ADDR_W83795})" + if ! [ -L ${dev_path} ]; then + echo "w83795adg ${I2C_ADDR_W83795}" > ${PATH_MUX_9548_1_CH7}/new_device #hwmon7 + else + echo "${dev_path} already exist" + fi + + # probe jc42 kernel module + modprobe jc42 + + echo "Done" +} + +#FAN Speed Init +function _i2c_fan_speed_init { + echo -n "FAN SPEED INIT..." + if [ -e "${PATH_HWMON_W83795_DEVICE}" ]; then + # init fan speed + echo 120 > ${PATH_HWMON_W83795_DEVICE}/device/pwm2 + echo "SUCCESS" + else + echo "FAIL" + fi +} + +# HWM init +function _i2c_hwm_init { + echo "HWM INIT..." + # select bank0 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x00 0x80 + # SW reset, Disable monitor + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x01 0x9C + # disable TR5/TR6 DTS + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x04 0x0 + # enable FANIN1~8 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x06 0xFF + # disable FANIN9~14 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x07 0x00 + # CLKIN clock frequency set as 48Mhz + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x01 0x1C + # select bank 2 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x00 0x82 + # set PWM mode in FOMC + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x0F 0x00 + # set 25KHz fan output frequency in F1OPFP&F2OPFP + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x18 0x84 + _util_i2cset -y -r ${I2C_BUS_HWM} ${I2C_ADDR_W83795} 0x19 0x84 +} + +#IO Expander Init +function _i2c_io_exp_init { + echo "=========================================================" + echo "# Description: I2C IO Expender Init" + echo "=========================================================" + + # need to init BMC io expander first due to some io expander are reset default + echo "Init BMC INT & HW ID IO Expander" + # all input + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_1} ${REG_PORT0_DIR} 0xFF + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_1} ${REG_PORT1_DIR} 0xFF + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_1} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_1} ${REG_PORT1_POL} 0x00 + + echo "Init BMC PSU status IO Expander" + # PWRON default 0 (ACTIVE_LOW) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_2} ${REG_PORT0_OUT} 0x00 + # default 0 (ACTIVE_LOW) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_2} ${REG_PORT1_OUT} 0x00 + # I/O 0.2 0.5 output(PWRON), rest input + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_2} ${REG_PORT0_DIR} 0xDB + # I/O 1.0~1.1 input, 1.2~1.4 output (1.5~1.7 not enable) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_2} ${REG_PORT1_DIR} 0xE3 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_2} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_2} ${REG_PORT1_POL} 0x00 + + echo "Init BMC RST and SEL IO Expander" + # RST default is 1 (ACTIVE_LOW) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_3} ${REG_PORT0_OUT} 0x3F + # SEL default is 0 (HOST), EN default is 1 (ACTIVE_HIGH) + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_3} ${REG_PORT1_OUT} 0x1F + # I/O 0.0~0.5 output, 0.6~0.7 not use + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_3} ${REG_PORT0_DIR} 0xC0 + # all output + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_3} ${REG_PORT1_DIR} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_3} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9555_3} ${REG_PORT1_POL} 0x00 + + echo "Init System LED & HW ID IO Expander" + # I/O_0.x for System LED default 0, I/O_1.x for HW ID + _util_i2cset -y -r ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${REG_PORT0_OUT} 0x00 + # System LED => all output + _util_i2cset -y -r ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${REG_PORT0_DIR} 0x00 + # HW ID => all input + _util_i2cset -y -r ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${REG_PORT1_DIR} 0xFF + _util_i2cset -y -r ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${REG_PORT1_POL} 0x00 + + echo "Init FAN Board Status IO Expander" + # LED_G_H set to 1, LED_Y_G set to 0 (ACTIVE_HIGH) + _util_i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9555_0} ${REG_PORT0_OUT} 0x11 + _util_i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9555_0} ${REG_PORT1_OUT} 0x11 + # DIR/ABS is input, LED_Y/LED_G is output + _util_i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9555_0} ${REG_PORT0_DIR} 0xCC + _util_i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9555_0} ${REG_PORT1_DIR} 0xCC + _util_i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9555_0} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_FAN_STATUS} ${I2C_ADDR_MUX_9555_0} ${REG_PORT1_POL} 0x00 + + echo "Init System SEL and RST IO Expander" + # RST 0.0~0.3 default 1 (ACTIVE low), rest default 0 + # SEL set to value 0 (host) + # LED_CLR set to 0 will clear all switch LED bitmap, set to 1 here + _util_i2cset -y -r ${NUM_MUX_9548_1_CH2} ${I2C_ADDR_MUX_9539_1} ${REG_PORT0_OUT} 0x0F + # RST 1.6~1.7 default 1 (ACTIVE low), INT 1.0~1.4 default 1 (ACTIVE low) + _util_i2cset -y -r ${NUM_MUX_9548_1_CH2} ${I2C_ADDR_MUX_9539_1} ${REG_PORT1_OUT} 0xDF + # all output, but MAC_RST_L 0.0 need to set as input to prevent reboot issue + _util_i2cset -y -r ${NUM_MUX_9548_1_CH2} ${I2C_ADDR_MUX_9539_1} ${REG_PORT0_DIR} 0x09 + # RST 1.5 !~ 1.7 output, rest are input + _util_i2cset -y -r ${NUM_MUX_9548_1_CH2} ${I2C_ADDR_MUX_9539_1} ${REG_PORT1_DIR} 0x1F + _util_i2cset -y -r ${NUM_MUX_9548_1_CH2} ${I2C_ADDR_MUX_9539_1} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${NUM_MUX_9548_1_CH2} ${I2C_ADDR_MUX_9539_1} ${REG_PORT1_POL} 0x00 + + echo "Init CPU CPLD IO Expander" + # all input + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_2} ${REG_PORT0_DIR} 0xFF + # all input + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_2} ${REG_PORT1_DIR} 0xFF + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_2} ${REG_PORT0_POL} 0x00 + _util_i2cset -y -r ${I2C_BUS_MAIN} ${I2C_ADDR_MUX_9539_2} ${REG_PORT1_POL} 0x00 +} + +#Set FAN Tray LED +function _i2c_led_fan_tray_status_set { + echo "FAN Tray Status Setup" + #FAN Status get + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN2_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN4_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + + # check if io expander for fan tray exist + result=`i2cget -y ${I2C_BUS_FANTRAY_LED} ${I2C_ADDR_MUX_9555_0} ${REG_PORT0_IN} 2>/dev/null` + err_code=$? + if [ "$err_code" != "0" ]; then + echo "fan tray not exist!" + return + fi + + for FAN_TRAY in {1..4}; + do + FAN_ALARM="FAN${FAN_TRAY}_ALARM" + if [ "${!FAN_ALARM}" == "0" ]; then + COLOR_LED="green" + ONOFF_LED="on" + _i2c_fan_tray_led + + COLOR_LED="amber" + ONOFF_LED="off" + _i2c_fan_tray_led + + echo "set [FAN TRAY ${FAN_TRAY}] [Green]=on [Amber]=off" + else + COLOR_LED="green" + ONOFF_LED="off" + _i2c_fan_tray_led + + COLOR_LED="amber" + ONOFF_LED="on" + _i2c_fan_tray_led + + echo "set [FAN TRAY ${FAN_TRAY}] [Green]=off [Amber]=on" + fi + done +} + +#Set FAN LED +function _i2c_led_fan_status_set { + echo "FAN Status Setup" + #PSU Status set + FAN1_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan1_alarm` + FAN3_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan3_alarm` + FAN5_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan5_alarm` + FAN7_ALARM=`cat ${PATH_HWMON_W83795_DEVICE}/device/fan7_alarm` + + echo "led_fan setup..." + # all fan ok + if [ "${FAN1_ALARM}" == "0" ] \ + && [ "${FAN3_ALARM}" == "0" ] \ + && [ "${FAN5_ALARM}" == "0" ] \ + && [ "${FAN7_ALARM}" == "0" ]; then + COLOR_LED="green" + echo "${COLOR_LED}" + _i2c_fan_led + # all fan fail + elif [ "${FAN1_ALARM}" == "1" ] \ + && [ "${FAN3_ALARM}" == "1" ] \ + && [ "${FAN5_ALARM}" == "1" ] \ + && [ "${FAN7_ALARM}" == "1" ] ; then + COLOR_LED="amber" + echo "${COLOR_LED}" + _i2c_fan_led + # partial fan fail + else + COLOR_LED="amber" + echo "${COLOR_LED}" + _i2c_fan_led + fi +} + +#Set Power Supply LED +function _i2c_led_psu_status_set { + echo "PSU LED Status Setup" + + #PSU Status set + _i2c_psu_status + + #PSU1 Status + echo "led_psu1 setup..." + if [ "${psu1Exist}" == ${PSU_EXIST} ]; then + if [ "${psu1PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + echo "${COLOR_LED}" + _i2c_psu1_led + else + COLOR_LED="amber" + echo "${COLOR_LED}" + _i2c_psu1_led + fi + else + COLOR_LED="off" + echo "${COLOR_LED}" + _i2c_psu1_led + fi + + #PSU2 Status + echo "led_psu2 setup..." + if [ "${psu2Exist}" == ${PSU_EXIST} ]; then + if [ "${psu2PwGood}" == ${PSU_DC_ON} ]; then + COLOR_LED="green" + echo "${COLOR_LED}" + _i2c_psu2_led + else + COLOR_LED="amber" + echo "${COLOR_LED}" + _i2c_psu2_led + fi + else + COLOR_LED="off" + echo "${COLOR_LED}" + _i2c_psu2_led + fi +} + +#LED Test +function _i2c_led_test { + echo "=========================================================" + echo "# Description: I2C SYSTEM LED TEST..." + echo "=========================================================" + local output_reg=${REG_PORT0_OUT} + local mask=0xFF + local value=0xFF + + #sys led (green) + # set sys_led_g (0.7) = 1 + mask=0x80 + value=0x80 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check SYS LED green light and Press [Enter] key to continue...' + #sys led (amber) + # set sys_led_g (0.7) = 0 + mask=0x80 + value=0x00 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check SYS LED amber light and Press [Enter] key to continue...' + + #FAN led (green) + # set fan_led_en (0.6) = 1 & fan_led_y (0.5) = 0 + mask=0x60 + value=0x60 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check FAN LED green light and Press [Enter] key to continue...' + #FAN led (amber) + # set fan_led_en (0.6) = 1 & fan_led_y (0.5) = 1 + mask=0x60 + value=0x40 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check FAN LED amber light and Press [Enter] key to continue...' + + #PSU1 led (green) + # set psu1_pwr_ok_oe (0.4) = 1 & psu1_led_y (0.3) = 0 + mask=0x18 + value=0x18 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check PSU1 LED green light and Press [Enter] key to continue...' + #PSU1 led (amber) + # set psu1_pwr_ok_oe (0.4) = 1 & psu1_led_y (0.3) = 1 + mask=0x18 + value=0x10 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check PSU1 LED amber light and Press [Enter] key to continue...' + + #PSU2 led (green) + # set psu0_pwr_ok_oe (0.2) = 1 & psu0_led_y (0.1) = 0 + mask=0x06 + value=0x06 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check PSU2 LED green light and Press [Enter] key to continue...' + #PSU2 led (amber) + # set psu0_pwr_ok_oe (0.2) = 1 & psu0_led_y (0.1) = 1 + mask=0x06 + value=0x04 + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check PSU2 LED amber light and Press [Enter] key to continue...' + + #Turn OFF All LED (can't trun off system led) + # set set fan_led_en (0.6), psu1_pwr_ok_oe (0.4), psu0_pwr_ok_oe (0.2) = 0 + mask=0xFF + value=$((2#00000011)) + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + _pause 'Check turn off all LEDs (exclude SYS LED) and Press [Enter] key to continue...' + + # restore sys led + COLOR_LED="green" + ONOFF_LED="on" + echo "${COLOR_LED} ${ONOFF_LED}" + _i2c_sys_led +} + +#Set QSFP Port cpld variable +function _qsfp_cpld_var_set { + local port=$1 + local reg_port_base + local reg_port_shift + if [[ $1 -le 12 && $1 -ge 1 ]]; then + cpld_index=1 + reg_port_base=0 + elif [[ $1 -le 25 && $1 -ge 13 ]]; then + cpld_index=2 + reg_port_base=12 + elif [[ $1 -le 38 && $1 -ge 26 ]]; then + cpld_index=3 + reg_port_base=25 + elif [[ $1 -le 51 && $1 -ge 39 ]]; then + cpld_index=4 + reg_port_base=38 + elif [[ $1 -le 64 && $1 -ge 52 ]]; then + cpld_index=5 + reg_port_base=51 + else + echo "invalid port number" + fi + + cpld_port_index=$(( $port - $reg_port_base )) +} + +#Set QSFP Port eeporm variable +function _qsfp_eeprom_var_set { + local port=$1 + local eeprombusbase + local eeprombusshift + # port 1 => zqsfp0 + # port 2 => zqsfp1 + # ... + local port_group=$(( ($port - 1) / 8 )) + + case ${port_group} in + 0) + eeprombusbase=${NUM_MUX_9548_3_CH0} + ;; + 1) + eeprombusbase=${NUM_MUX_9548_4_CH0} + ;; + 2) + eeprombusbase=${NUM_MUX_9548_5_CH0} + ;; + 3) + eeprombusbase=${NUM_MUX_9548_6_CH0} + ;; + 4) + eeprombusbase=${NUM_MUX_9548_7_CH0} + ;; + 5) + eeprombusbase=${NUM_MUX_9548_8_CH0} + ;; + 6) + eeprombusbase=${NUM_MUX_9548_9_CH0} + ;; + 7) + eeprombusbase=${NUM_MUX_9548_10_CH0} + ;; + *) + ;; + esac + + eeprombusshift=$(( (${port} - 1) % 8)) + eepromBus=$(( ${eeprombusbase} + ${eeprombusshift} )) + eepromAddr=${I2C_ADDR_QSFP_EEPROM} +} + + +#Set SFP Port cpld variable +function _sfp_cpld_var_set { + local port=$1 + case ${port} in + 1) + cpld_index=1 + ;; + 2) + cpld_index=2 + ;; + *) + ;; + esac +} + +#Set QSFP Port eeporm variable +function _sfp_eeprom_var_set { + local port=$1 + + case ${port} in + 1) + eepromBus=${NUM_MUX_9546_1_CH0} + ;; + 2) + eepromBus=${NUM_MUX_9546_1_CH1} + ;; + *) + ;; + esac + + eepromAddr=${I2C_ADDR_SFP_EEPROM} +} + +#Get QSFP EEPROM Information +function _i2c_qsfp_eeprom_get { + local phy_port=0 + + #get physical port + phy_port=$(_port_logic2phy $QSFP_PORT) + + # input parameter validation + _util_input_check ${QSFP_PORT} ${MIN_QSFP_PORT_NUM} ${MAX_QSFP_PORT_NUM} + + _qsfp_eeprom_var_set ${phy_port} + + _util_get_qsfp_abs + + if [ $status = 0 ]; then + exit + fi + + + cat ${PATH_SYS_I2C_DEVICES}/$eepromBus-$(printf "%04x" $eepromAddr)/eeprom | hexdump -C +} + +#Init QSFP EEPROM +function _i2c_qsfp_eeprom_init { + echo "QSFP EEPROM INIT..." + + #Action check + action=$1 + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init 1-64 ports EEPROM + local i + for i in {1..64}; + do + _qsfp_eeprom_var_set ${i} + + if [ "${action}" == "new" ] && \ + ! [ -L ${PATH_SYS_I2C_DEVICES}/$eepromBus-$(printf "%04x" $eepromAddr) ]; then + echo "sff8436 $eepromAddr" > ${PATH_SYS_I2C_DEVICES}/i2c-$eepromBus/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${PATH_SYS_I2C_DEVICES}/$eepromBus-$(printf "%04x" $eepromAddr) ]; then + echo "$eepromAddr" > ${PATH_SYS_I2C_DEVICES}/i2c-$eepromBus/delete_device + fi + done + echo "Done" +} + +#Get SFP EEPROM Information +function _i2c_sfp_eeprom_get { + + # input parameter validation + _util_input_check ${SFP_PORT} ${MIN_SFP_PORT_NUM} ${MAX_SFP_PORT_NUM} + _util_get_sfp_pres + + if [ $status = 0 ]; then + exit + fi + + _sfp_eeprom_var_set ${SFP_PORT} + + cat ${PATH_SYS_I2C_DEVICES}/$eepromBus-$(printf "%04x" $eepromAddr)/eeprom | hexdump -C +} + +#Init SFP EEPROM +function _i2c_sfp_eeprom_init { + echo "SFP EEPROM INIT..." + + #Action check + action=$1 + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init 1-2 sfp+ EEPROM + local i + for i in {1..2}; + do + _sfp_eeprom_var_set ${i} + + if [ "${action}" == "new" ] && \ + ! [ -L ${PATH_SYS_I2C_DEVICES}/$eepromBus-$(printf "%04x" $eepromAddr) ]; then + echo "sff8436 $eepromAddr" > ${PATH_SYS_I2C_DEVICES}/i2c-$eepromBus/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${PATH_SYS_I2C_DEVICES}/$eepromBus-$(printf "%04x" $eepromAddr) ]; then + echo "$eepromAddr" > ${PATH_SYS_I2C_DEVICES}/i2c-$eepromBus/delete_device + fi + done + echo "Done" +} + +#Init Main Board EEPROM +function _i2c_mb_eeprom_init { + echo -n "Main Board EEPROM INIT..." + + #Action check + action=$1 + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init mb EEPROM + if [ "${action}" == "new" ] && \ + ! [ -L ${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MB_EEPROM}-$(printf "%04x" $I2C_ADDR_MB_EEPROM) ]; then + echo "mb_eeprom ${I2C_ADDR_MB_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_MB_EEPROM}/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MB_EEPROM}-$(printf "%04x" $I2C_ADDR_MB_EEPROM) ]; then + echo "$I2C_ADDR_MB_EEPROM" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_MB_EEPROM}/delete_device + fi + echo "Done" +} + +#Init CPU Board EEPROM +function _i2c_cb_eeprom_init { + echo -n "CPU Board EEPROM INIT..." + + #Action check + action=$1 + if [ -z "${action}" ]; then + echo "No action, skip" + return + elif [ "${action}" != "new" ] && [ "${action}" != "delete" ]; then + echo "Error action, skip" + return + fi + + #Init cpu EEPROM + if [ "${action}" == "new" ] && \ + ! [ -L ${PATH_SYS_I2C_DEVICES}/${I2C_BUS_CB_EEPROM}-$(printf "%04x" $I2C_ADDR_CB_EEPROM) ]; then + echo "mb_eeprom ${I2C_ADDR_CB_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_CB_EEPROM}/new_device + elif [ "${action}" == "delete" ] && \ + [ -L ${PATH_SYS_I2C_DEVICES}/${I2C_BUS_CB_EEPROM}-$(printf "%04x" $I2C_ADDR_CB_EEPROM) ]; then + echo "$I2C_ADDR_CB_EEPROM" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_CB_EEPROM}/delete_device + fi + echo "Done" +} + +#Init PSU Kernel Module +function _i2c_psu_init { + echo "=========================================================" + echo "# Description: I2C PSU Init" + echo "=========================================================" + modprobe ingrasys_s9230_64x_psu + + echo "psu1 ${I2C_ADDR_PSU1_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_PSU1_EEPROM}/new_device + echo "psu2 ${I2C_ADDR_PSU2_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_PSU2_EEPROM}/new_device +} + +#Deinit PSU Kernel Module +function _i2c_psu_deinit { + echo "${I2C_ADDR_PSU1_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_PSU1_EEPROM}/delete_device + echo "${I2C_ADDR_PSU2_EEPROM}" > ${PATH_SYS_I2C_DEVICES}/i2c-${I2C_BUS_PSU2_EEPROM}/delete_device + _util_rmmod ingrasys_s9230_64x_psu +} + +#get QSFP Status +function _i2c_qsfp_status_get { + + # input parameter validation + _util_input_check ${QSFP_PORT} ${MIN_QSFP_PORT_NUM} ${MAX_QSFP_PORT_NUM} + + local stat + _util_get_qsfp_abs + echo "status=$status" +} + +#get QSFP Type +function _i2c_qsfp_type_get { + local phy_port=0 + + phy_port=$(_port_logic2phy ${QSFP_PORT}) + + # input parameter validation + _util_input_check ${QSFP_PORT} ${MIN_QSFP_PORT_NUM} ${MAX_QSFP_PORT_NUM} + + #_qsfp_eeprom_var_set ${QSFP_PORT} + _qsfp_eeprom_var_set ${phy_port} + + #Get QSFP EEPROM info + qsfp_info=$(base64 ${PATH_SYS_I2C_DEVICES}/$eepromBus-$(printf "%04x" $eepromAddr)/eeprom) + + identifier=$(echo $qsfp_info | base64 -d -i | hexdump -s 128 -n 1 -e '"%x"') + connector=$(echo $qsfp_info | base64 -d -i | hexdump -s 130 -n 1 -e '"%x"') + transceiver=$(echo $qsfp_info | base64 -d -i | hexdump -s 131 -n 1 -e '"%x"') + + echo "identifier=$identifier" + echo "connector=$connector" + echo "transceiver=$transceiver" +} + +function _i2c_sfp_status_get { + + # input parameter validation + _util_input_check ${SFP_PORT} ${MIN_SFP_PORT_NUM} ${MAX_SFP_PORT_NUM} + + local stat + _util_get_sfp_pres + #status: 0 -> Down, 1 -> Up + if [ $status = 0 ]; then + stat="down" + else + stat="up" + fi + echo "status is $stat" +} + +#get SFP Type +function _i2c_sfp_type_get { + + # input parameter validation + _util_input_check ${SFP_PORT} ${MIN_SFP_PORT_NUM} ${MAX_SFP_PORT_NUM} + + _sfp_eeprom_var_set ${SFP_PORT} + + #Get QSFP EEPROM info + sfp_info=$(base64 ${PATH_SYS_I2C_DEVICES}/$eepromBus-$(printf "%04x" $eepromAddr)/eeprom) + + identifier=$(echo $sfp_info | base64 -d -i | hexdump -s 128 -n 1 -e '"%x"') + connector=$(echo $sfp_info | base64 -d -i | hexdump -s 130 -n 1 -e '"%x"') + transceiver=$(echo $sfp_info | base64 -d -i | hexdump -s 131 -n 1 -e '"%x"') + + echo "identifier=$identifier" + echo "connector=$connector" + echo "transceiver=$transceiver" +} + +#Get PSU EEPROM Information +function _i2c_psu_eeprom_get { + local eeprom_psu1="" + local eeprom_psu2="" + + echo "=========================================================" + echo "# Description: I2C PSU EEPROM Get..." + echo "=========================================================" + + eeprom_psu1="${PATH_SYSFS_PSU1}/psu_eeprom" + cat ${eeprom_psu1} | hexdump -C + + eeprom_psu2="${PATH_SYSFS_PSU2}/psu_eeprom" + cat ${eeprom_psu2} | hexdump -C +} + +#Get Main Board EEPROM Information +function _i2c_mb_eeprom_get { + echo "=========================================================" + echo "# Description: I2C MB EEPROM Get..." + echo "=========================================================" + _i2c_sys_eeprom_get mb +} + +#Get CPU Board EEPROM Information +function _i2c_cb_eeprom_get { + echo "=========================================================" + echo "# Description: I2C CB EEPROM Get..." + echo "=========================================================" + _i2c_sys_eeprom_get cb +} + +#Get system EEPROM Information +##input: "cb" for cpu board, "mb" for main board +function _i2c_sys_eeprom_get { + local eeprom_dev + + if [ "$1" == "cb" ]; then + eeprom_dev="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_CB_EEPROM}-$(printf "%04x" $I2C_ADDR_CB_EEPROM)/eeprom" + elif [ "$1" == "mb" ]; then + eeprom_dev="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MB_EEPROM}-$(printf "%04x" $I2C_ADDR_MB_EEPROM)/eeprom" + else + echo "wrong eeprom type" + return + fi + + # check if eeprom device exist in sysfs + if [ ! -f ${eeprom_dev} ]; then + echo "eeprom device not init" + return + fi + + cat ${eeprom_dev} | hexdump -C + echo "Done" +} + +#sync eeprom content between mb and cb eeprom +function _i2c_eeprom_sync { + echo "=========================================================" + echo "# Description: EEPROM sync..." + echo "=========================================================" + + local mb_eeprom_dev="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_MB_EEPROM}-$(printf "%04x" $I2C_ADDR_MB_EEPROM)/eeprom" + local cb_eeprom_dev="${PATH_SYS_I2C_DEVICES}/${I2C_BUS_CB_EEPROM}-$(printf "%04x" $I2C_ADDR_CB_EEPROM)/eeprom" + + # check if eeprom device exist in sysfs + if [[ ! -f ${mb_eeprom_dev} || ! -f ${cb_eeprom_dev} ]]; then + echo "eeprom device not init" + return + fi + + ## check if MB eeprom is empty + if [ ! -z "$(cat ${mb_eeprom_dev} | hexdump -n2 | grep ffff)" ]; then + echo "copy cb eeprom to mb eeprom..." + cat ${cb_eeprom_dev} > ${mb_eeprom_dev} + else + echo "no need to sync" + fi + + echo "Done" +} + +#Set System Status LED +function _i2c_sys_led { + # only green/amber, on/off can't control + if [ "${COLOR_LED}" == "green" ]; then + # set sys_led_g (0.7) = 1 + output_reg=${REG_PORT0_OUT} + mask=0x80 + value=0x80 + elif [ "${COLOR_LED}" == "amber" ]; then + # set sys_led_g (0.7) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x80 + value=0x00 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + #apply to io expander + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + echo "Done" +} + +#Set FAN LED +function _i2c_fan_led { + if [ "${COLOR_LED}" == "green" ]; then + # set fan_led_en (0.6) = 1 & fan_led_y (0.5) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x60 + value=0x60 + elif [ "${COLOR_LED}" == "amber" ]; then + # set fan_led_en (0.6) = 1 & fan_led_y (0.5) = 1 + output_reg=${REG_PORT0_OUT} + mask=0x60 + value=0x40 + elif [ "${COLOR_LED}" == "off" ]; then + # set fan_led_en (0.6) = 0 & fan_led_y (0.5) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x60 + value=0x00 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + #apply to io expander + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + echo "Done" +} + +#Set PSU1 LED +function _i2c_psu1_led { + if [ "${COLOR_LED}" == "green" ]; then + # set psu1_pwr_ok_oe (0.4) = 1 & psu1_led_y (0.3) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x18 + value=0x18 + elif [ "${COLOR_LED}" == "amber" ]; then + # set psu1_pwr_ok_oe (0.4) = 1 & psu1_led_y (0.3) = 1 + output_reg=${REG_PORT0_OUT} + mask=0x18 + value=0x10 + elif [ "${COLOR_LED}" == "off" ]; then + # set psu1_pwr_ok_oe (0.4) = 0 & psu1_led_y (0.3) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x18 + value=0x00 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + #apply to io expander + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + echo "Done" +} + +#Set PSU2 LED +function _i2c_psu2_led { + if [ "${COLOR_LED}" == "green" ]; then + # set psu0_pwr_ok_oe (0.2) = 1 & psu0_led_y (0.1) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x06 + value=0x06 + elif [ "${COLOR_LED}" == "amber" ]; then + # set psu0_pwr_ok_oe (0.2) = 1 & psu0_led_y (0.1) = 1 + output_reg=${REG_PORT0_OUT} + mask=0x06 + value=0x04 + elif [ "${COLOR_LED}" == "off" ]; then + # set psu0_pwr_ok_oe (0.2) = 0 & psu0_led_y (0.1) = 0 + output_reg=${REG_PORT0_OUT} + mask=0x06 + value=0x00 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + #apply to io expander + _util_i2cset -m ${mask} -y ${I2C_BUS_SYS_LED} ${I2C_ADDR_MUX_9539_0} ${output_reg} ${value} + echo "Done" +} + +#Set FAN Tray LED +function _i2c_fan_tray_led { + + i2cAddr=${I2C_ADDR_MUX_9555_0} + output_reg=${REG_PORT0_OUT} + + case ${FAN_TRAY} in + 1) + output_reg=${REG_PORT0_OUT} + if [ "${COLOR_LED}" == "green" ]; then + mask=0x01 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x02 + fi + ;; + 2) + output_reg=${REG_PORT0_OUT} + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + 3) + output_reg=${REG_PORT1_OUT} + if [ "${COLOR_LED}" == "green" ]; then + mask=0x01 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x02 + fi + ;; + 4) + output_reg=${REG_PORT1_OUT} + if [ "${COLOR_LED}" == "green" ]; then + mask=0x10 + elif [ "${COLOR_LED}" == "amber" ]; then + mask=0x20 + fi + ;; + *) + echo "Please input 1~4" + exit + ;; + esac + + # LED PIN value is ACTIVE HIGH + if [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "on" ]; then + _util_i2cset -m $mask -y -r ${I2C_BUS_FANTRAY_LED} $i2cAddr ${output_reg} 0xff + elif [ "${COLOR_LED}" == "green" ] && [ "${ONOFF_LED}" == "off" ]; then + _util_i2cset -m $mask -y -r ${I2C_BUS_FANTRAY_LED} $i2cAddr ${output_reg} 0x00 + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "on" ]; then + _util_i2cset -m $mask -y -r ${I2C_BUS_FANTRAY_LED} $i2cAddr ${output_reg} 0xff + elif [ "${COLOR_LED}" == "amber" ] && [ "${ONOFF_LED}" == "off" ]; then + _util_i2cset -m $mask -y -r ${I2C_BUS_FANTRAY_LED} $i2cAddr ${output_reg} 0x00 + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + echo "Done" +} + +#Get Board Version and Type +function _i2c_board_type_get { + # read input port 1 value from io expander + input_reg=${REG_PORT1_IN} + boardType=`i2cget -y ${I2C_BUS_HW_ID} ${I2C_ADDR_MUX_9539_0} ${input_reg}` + boardBuildRev=$((($boardType) & 0x03)) + boardHwRev=$((($boardType) >> 2 & 0x03)) + boardId=$((($boardType) >> 4)) + printf "MAIN_BOARD BOARD_ID is 0x%02x, HW Rev 0x%02x, Build Rev 0x%02x\n" $boardId $boardHwRev $boardBuildRev +} + +#Get BMC Board Version and Type +function _i2c_bmc_board_type_get { + # read input port 1 value from io expander + input_reg=${REG_PORT1_IN} + boardType=`i2cget -y ${I2C_BUS_BMC_HW_ID} ${I2C_ADDR_MUX_9555_1} ${input_reg}` + boardBuildRev=$((($boardType) & 0x03)) + boardHwRev=$((($boardType) >> 2 & 0x03)) + boardId=$((($boardType) >> 4)) + printf "BMC_BOARD BOARD_ID is 0x%02x, HW Rev 0x%02x, Build Rev 0x%02x\n" $boardId $boardHwRev $boardBuildRev +} + +#Get CPLD Version +function _i2c_cpld_version { + echo "=========================================================" + echo "# Description: CPLD Version" + echo "=========================================================" + + for i in {1..5}; + do + local cpld_bus="I2C_BUS_CPLD${i}" + local cpld_path="PATH_CPLD${i}_DEVICE" + local file_path="${PATH_SYS_I2C_DEVICES}/${!cpld_bus}-$(printf "%04x" ${I2C_ADDR_CPLD})/cpld_version" + printf "[CPLD %d] %s\n" ${i} $(cat ${file_path}) + done +} + +#Get PSU Status +function _i2c_psu_status { + local psu_abs="" + + psu1PwGood=`cat ${PATH_SYSFS_PSU1}/psu_pg` + psu_abs=`cat ${PATH_SYSFS_PSU1}/psu_abs` + if [ "$psu_abs" == "0" ]; then + psu1Exist=1 + else + psu1Exist=0 + fi + + psu2PwGood=`cat ${PATH_SYSFS_PSU2}/psu_pg` + psu_abs=`cat ${PATH_SYSFS_PSU2}/psu_abs` + if [ "$psu_abs" == "0" ]; then + psu2Exist=1 + else + psu2Exist=0 + fi + + printf "PSU1 Exist:%x PSU1 PW Good:%d\n" $psu1Exist $psu1PwGood + printf "PSU2 Exist:%d PSU2 PW Good:%d\n" $psu2Exist $psu2PwGood +} + +#Get register value from CPLD +function _i2c_cpld_reg_read { + local idx=$1 + local file_name=$2 + local cpld_i2c_addr=${I2C_ADDR_CPLD} + local reg_file_path + + case ${idx} in + 1) + cpld_i2c_bus=${I2C_BUS_CPLD1} + ;; + 2) + cpld_i2c_bus=${I2C_BUS_CPLD2} + ;; + 3) + cpld_i2c_bus=${I2C_BUS_CPLD3} + ;; + 4) + cpld_i2c_bus=${I2C_BUS_CPLD4} + ;; + 5) + cpld_i2c_bus=${I2C_BUS_CPLD5} + ;; + *) + echo "invalid cpld index" + exit + ;; + esac + + reg_file_path="${PATH_SYS_I2C_DEVICES}/${cpld_i2c_bus}-$(printf "%04x" ${cpld_i2c_addr})/${file_name}" + cpld_reg_val=`cat ${reg_file_path}` +} + +#util functions +function _util_i2cset { + if [ "$DEBUG" == "on" ]; then + i2cset $@ + else + i2cset $@ 1>/dev/null + fi +} + +function _i2c_set { + local i2c_bus=$1 + local i2c_addr=$2 + local reg=$3 + local mask=$4 + local value=$5 + + echo `i2cset -m $mask -y -r ${i2c_bus} ${i2c_addr} ${reg} ${value}` +} + +function _util_rmmod { + local mod=$1 + [ "$(lsmod | grep "^$mod ")" != "" ] && rmmod $mod +} + +# get qsfp presence +function _util_get_qsfp_abs { + local phy_port=0 + + #get physical port + phy_port=$(_port_logic2phy $QSFP_PORT) + + # read status from cpld + #_qsfp_cpld_var_set ${QSFP_PORT} + _qsfp_cpld_var_set ${phy_port} + cpld_reg_file_name="${CPLD_QSFP_STATUS_KEY}_${cpld_port_index}" + _i2c_cpld_reg_read ${cpld_index} ${cpld_reg_file_name} + #status: 0 -> Down, 1 -> Up (ACTIVE_LOW in ABS_BIT) + status=$(( $(( $((${cpld_reg_val})) & (1 << ($CPLD_QSFP_STATUS_ABS_BIT)) ))?0:1 )) +} + +# get sfp presence +function _util_get_sfp_pres { + + # read status from cpld + _sfp_cpld_var_set ${SFP_PORT} + cpld_reg_file_name="${CPLD_SFP_STATUS_KEY}" + _i2c_cpld_reg_read ${cpld_index} ${cpld_reg_file_name} + #status: 0 -> Down, 1 -> Up (ACTIVE_LOW in PRES_BIT) + status=$(( $(( $((${cpld_reg_val})) & (1 << ($CPLD_SFP_STATUS_PRES_BIT)) ))?0:1 )) +} + +# valid input number +function _util_input_check { + # input parameter validation + if [[ $1 -lt $2 || $1 -gt $3 ]]; then + echo "Please input number $2~$3" + exit + fi +} + +# clear all switch port led bitmap +function _util_port_led_clear { + echo "port led clear..." + # gpio pin on GPIO MUX PCA9539#1 I/O 0.2 + # pull low to reset bitamp + output_reg=${REG_PORT0_OUT} + mask=0x04 + value=0x00 + _util_i2cset -m ${mask} -y ${NUM_MUX_9548_1_CH2} ${I2C_ADDR_MUX_9539_1} ${output_reg} ${value} + echo "Done" +} + +#Increase read socket buffer for CoPP Test +function _config_rmem { + echo "109430400" > /proc/sys/net/core/rmem_max +} + +#Set 10G Mux to CPU or Front Panel +function _i2c_10g_mux { + + echo "=========================================================" + echo "# Description: 10G Mux" + echo "=========================================================" + + local target=${TARGET_10G_MUX} + local i2c_bus=${I2C_BUS_10GMUX} + local i2c_addr=${I2C_ADDR_10GMUX} + local reg=0 + local mask=0xff + local value=0 + local file_10g_mux="${PATH_CPLD1_DEVICE}/${I2C_BUS_CPLD1}-$(printf "%04x" ${I2C_ADDR_CPLD})/cpld_10gmux_config" + + #get current 10g mux register value on CPLD + value=`cat ${file_10g_mux}` + + if [ "${target}" == "cpu" ]; then + #Set XE0: slave address access + _i2c_set $i2c_bus $i2c_addr 0x06 $mask 0x18 + #Set XE0: CH4 D_IN0-S_OUTA0 EQ + _i2c_set $i2c_bus $i2c_addr 0x2c $mask 0x00 + #Set XE0: CH5 NC-S_OUTB0 DEM + _i2c_set $i2c_bus $i2c_addr 0x35 $mask 0x80 + #Set XE0: CH5 NC-S_OUTB0 VOD + _i2c_set $i2c_bus $i2c_addr 0x34 $mask 0xab + #Set XE0: CH1 D_OUT0-S_INB0 EQ + _i2c_set $i2c_bus $i2c_addr 0x16 $mask 0x01 + #Set XE0: CH1 D_OUT0-S_INB0 DEM + _i2c_set $i2c_bus $i2c_addr 0x18 0x1f 0x80 + #Set XE0: CH1 D_OUT0-S_INB0 VOD + _i2c_set $i2c_bus $i2c_addr 0x17 $mask 0xab + #Set XE1: CH6 D_IN1-S_OUTA1 EQ + _i2c_set $i2c_bus $i2c_addr 0x3a $mask 0x00 + #Set XE1: CH7 NC-S_OUTB1 EQ + _i2c_set $i2c_bus $i2c_addr 0x41 $mask 0x00 + #Set XE1: CH7 NC-S_OUTB1 DEM + _i2c_set $i2c_bus $i2c_addr 0x43 $mask 0x80 + #Set XE1: CH7 NC-S_OUTB1 VOD + _i2c_set $i2c_bus $i2c_addr 0x42 $mask 0xab + #Set XE1: CH3 D_OUT1-S_INB1 EQ + _i2c_set $i2c_bus $i2c_addr 0x24 $mask 0x01 + #Set XE1: CH3 D_OUT1-S_INB1 DEM + _i2c_set $i2c_bus $i2c_addr 0x26 $mask 0x80 + #Set XE1: CH3 D_OUT1-S_INB1 VOD + _i2c_set $i2c_bus $i2c_addr 0x25 $mask 0xab + #Enable auto negotiation of XE ports + eval "npx_diag 'phy set mdio portlist=129,130 devad=0x7 addr=0x11 data=0x80'" + eval "npx_diag 'phy set mdio portlist=129,130 devad=0x7 addr=0x10 data=0x01'" + eval "npx_diag 'phy set mdio portlist=129,130 devad=0x7 addr=0x0 data=0x3200'" + eval "npx_diag 'phy set mdio portlist=129,130 devad=0x1 addr=0x96 data=0x2'" + #delay 1 sec + sleep 1 + + #switch 10G MUX to cpu on CPLD + value=$(( value & 0xf3 )) + echo "$(printf 0x"%x" ${value})" > ${file_10g_mux} + + echo "Switch 10G Mux to [CPU]" + + elif [ "${COLOR_LED}" == "fp" ]; then + #Set XE0: slave address access + _i2c_set $i2c_bus $i2c_addr 0x06 $mask 0x18 + #Set XE0: CH4 D_IN0-S_OUTA0 EQ + _i2c_set $i2c_bus $i2c_addr 0x2c $mask 0x00 + #Set XE0: CH4 D_IN0-S_OUTA0 DEM + _i2c_set $i2c_bus $i2c_addr 0x2e $mask 0x80 + #Set XE0: CH4 D_IN0-S_OUTA0 VOD + _i2c_set $i2c_bus $i2c_addr 0x2d $mask 0xab + #Set XE0: CH0 NC-S_INA0 EQ + _i2c_set $i2c_bus $i2c_addr 0x0f $mask 0x00 + #Set XE0: CH1 D_OUT0-S_INB0 DEM + _i2c_set $i2c_bus $i2c_addr 0x18 0x18 0x80 + #Set XE0: CH1 D_OUT0-S_INB0 VOD + _i2c_set $i2c_bus $i2c_addr 0x17 $mask 0xab + #Set XE1: CH6 D_IN1-S_OUTA1 EQ + _i2c_set $i2c_bus $i2c_addr 0x3a $mask 0x00 + #Set XE1: CH6 D_IN1-S_OUTA1 DEM + _i2c_set $i2c_bus $i2c_addr 0x3c $mask 0x80 + #Set XE1: CH6 D_IN1-S_OUTA1 VOD + _i2c_set $i2c_bus $i2c_addr 0x3b $mask 0xab + #Set XE1: CH2 NC-S_INA1 EQ + _i2c_set $i2c_bus $i2c_addr 0x1d $mask 0x00 + #Set XE1: CH3 D_OUT1-S_INB1 DEM + _i2c_set $i2c_bus $i2c_addr 0x26 $mask 0x80 + #Set XE1: CH3 D_OUT1-S_INB1 VOD + _i2c_set $i2c_bus $i2c_addr 0x25 $mask 0xab + #Enable auto negotiation of XE ports + eval "npx_diag 'phy set mdio portlist=129,130 devad=0x7 addr=0x11 data=0x0'" + eval "npx_diag 'phy set mdio portlist=129,130 devad=0x7 addr=0x10 data=0x0'" + eval "npx_diag 'phy set mdio portlist=129,130 devad=0x7 addr=0x0 data=0x2200'" + eval "npx_diag 'phy set mdio portlist=129,130 devad=0x1 addr=0x96 data=0x2200'" + #delay 1 sec + sleep 1 + + #switch 10G MUX to front panel on CPLD + value=$(( value | 0x0c )) + echo "$(printf 0x"%x" ${value})" > ${file_10g_mux} + + echo "Switch 10G Mux to [Front Panel]" + else + echo "invalid target, please set target to cpu/fp" + fi +} + +#Main Function +function _main { + tart_time_str=`date` + start_time_sec=$(date +%s) + + if [ "${EXEC_FUNC}" == "help" ]; then + _help + elif [ "${EXEC_FUNC}" == "i2c_init" ]; then + _i2c_init + elif [ "${EXEC_FUNC}" == "i2c_deinit" ]; then + _i2c_deinit + elif [ "${EXEC_FUNC}" == "i2c_fan_speed_init" ]; then + _i2c_fan_speed_init + elif [ "${EXEC_FUNC}" == "i2c_io_exp_init" ]; then + _i2c_io_exp_init + elif [ "${EXEC_FUNC}" == "i2c_led_test" ]; then + _i2c_led_test + elif [ "${EXEC_FUNC}" == "i2c_mb_eeprom_get" ]; then + _i2c_mb_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_cb_eeprom_get" ]; then + _i2c_cb_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_eeprom_sync" ]; then + _i2c_eeprom_sync + elif [ "${EXEC_FUNC}" == "i2c_psu_eeprom_get" ]; then + _i2c_psu_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_eeprom_get" ]; then + _i2c_qsfp_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_sfp_eeprom_get" ]; then + _i2c_sfp_eeprom_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_eeprom_init" ]; then + _i2c_qsfp_eeprom_init ${QSFP_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_sfp_eeprom_init" ]; then + _i2c_sfp_eeprom_init ${SFP_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_mb_eeprom_init" ]; then + _i2c_mb_eeprom_init ${MB_EEPROM_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_cb_eeprom_init" ]; then + _i2c_cb_eeprom_init ${MB_EEPROM_ACTION} + elif [ "${EXEC_FUNC}" == "i2c_qsfp_status_get" ]; then + _i2c_qsfp_status_get + elif [ "${EXEC_FUNC}" == "i2c_sfp_status_get" ]; then + _i2c_sfp_status_get + elif [ "${EXEC_FUNC}" == "i2c_qsfp_type_get" ]; then + _i2c_qsfp_type_get + elif [ "${EXEC_FUNC}" == "i2c_sfp_type_get" ]; then + _i2c_sfp_type_get + elif [ "${EXEC_FUNC}" == "i2c_led_psu_status_set" ]; then + _i2c_led_psu_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_status_set" ]; then + _i2c_led_fan_status_set + elif [ "${EXEC_FUNC}" == "i2c_led_fan_tray_status_set" ]; then + _i2c_led_fan_tray_status_set + elif [ "${EXEC_FUNC}" == "i2c_sys_led" ]; then + _i2c_sys_led + elif [ "${EXEC_FUNC}" == "i2c_fan_led" ]; then + _i2c_fan_led + elif [ "${EXEC_FUNC}" == "i2c_fan_tray_led" ]; then + _i2c_fan_tray_led + elif [ "${EXEC_FUNC}" == "i2c_psu1_led" ]; then + _i2c_psu1_led + elif [ "${EXEC_FUNC}" == "i2c_psu2_led" ]; then + _i2c_psu2_led + elif [ "${EXEC_FUNC}" == "i2c_board_type_get" ]; then + _i2c_board_type_get + elif [ "${EXEC_FUNC}" == "i2c_bmc_board_type_get" ]; then + _i2c_bmc_board_type_get + elif [ "${EXEC_FUNC}" == "i2c_cpld_version" ]; then + _i2c_cpld_version + elif [ "${EXEC_FUNC}" == "i2c_psu_status" ]; then + _i2c_psu_status + elif [ "${EXEC_FUNC}" == "i2c_test_all" ]; then + _i2c_init + _i2c_led_test + _i2c_psu_eeprom_get + _i2c_mb_eeprom_get + _i2c_cb_eeprom_get + _i2c_board_type_get + _i2c_bmc_board_type_get + _i2c_cpld_version + _i2c_psu_status + elif [ "${EXEC_FUNC}" == "i2c_10g_mux" ]; then + _i2c_10g_mux + else + echo "Invalid Parameters, Exit!!!" + _help + exit ${FALSE} + fi + + if [ "$DEBUG" == "on" ]; then + echo "-----------------------------------------------------" + end_time_str=`date` + end_time_sec=$(date +%s) + diff_time=$[ ${end_time_sec} - ${start_time_sec} ] + echo "Start Time: ${start_time_str} (${start_time_sec})" + echo "End Time : ${end_time_str} (${end_time_sec})" + echo "Total Execution Time: ${diff_time} sec" + + echo "done!!!" + fi +} + +_main diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/utils/qsfp_monitor.sh b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/utils/qsfp_monitor.sh new file mode 100755 index 000000000000..23a3fd066bee --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/utils/qsfp_monitor.sh @@ -0,0 +1,105 @@ +#!/bin/bash +# Copyright (C) 2017 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=3 +I2C_UTILS="/usr/sbin/i2c_utils.sh" +QSFP_SI_SCRIPT="/usr/sbin/qsfp_si_cfg.sh" +QSFP_ARRAY=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \ + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + +#QSFP SI monitor +function _qsfp_si_monitor { + local i + local status + for i in {0..63}; + do + status=`${I2C_UTILS} i2c_qsfp_status_get $(expr $i + 1) | egrep '^status=.*$' | sed -e 's/status=//g'` + if [ "${status}" == "1" ]; then + _qsfp_type_check $i + fi + done +} + +#QSFP type +function _qsfp_type_check { + local port=$1 + local qsfp_type=`${I2C_UTILS} i2c_qsfp_type_get $(expr $port + 1)` + local identifier=`echo "$qsfp_type" | grep '^identifier=.*$' | sed -e 's/identifier=//g'` + if [ "${identifier}" == "11" ]; then + connector=`echo "$qsfp_type" | grep '^connector=.*$' | sed -e 's/connector=//g'` + case ${connector} in + 21|23) + #DAC + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to DAC" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} dac $port >/dev/null + fi + ;; + *) + #Optical + if [ "${QSFP_ARRAY[$port]}" != "${connector}" ]; then + echo "Change Port $(expr $port + 1) to Optical" + QSFP_ARRAY[$port]=${connector} + ${QSFP_SI_SCRIPT} optical $port >/dev/null + fi + ;; + esac + fi +} + +#Docker exist check +function _docker_swss_check { + while true + do + # Check if syncd starts + result=`docker exec -i swss bash -c "echo -en \"SELECT 1\\nHLEN HIDDEN\" | redis-cli | sed -n 2p"` #TBD FIX ME + if [ "$result" == "3" ]; then + return + fi + sleep $INTERVAL + done +} + +#Docker exist check +function _qsfp_si_cfg_script_check { + + if [ -f ${QSFP_SI_SCRIPT} ] && [ -x ${QSFP_SI_SCRIPT} ]; then + echo "SI Script exists. Start monitor." + return + else + echo "SI Script not exist. Exit monitor." + exit + fi +} + +# main function +function _main { + #Check SI Script + _qsfp_si_cfg_script_check + #Check docker swss is running + _docker_swss_check + while true + do + _qsfp_si_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main + diff --git a/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/utils/s9230_64x_monitor.sh b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/utils/s9230_64x_monitor.sh new file mode 100755 index 000000000000..8bfb22328003 --- /dev/null +++ b/platform/nephos/sonic-platform-modules-ingrasys/s9230-64x/utils/s9230_64x_monitor.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright (C) 2016 Ingrasys, Inc. +# +# 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 3 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, see . + +INTERVAL=5 +I2C_UTILS="/usr/sbin/i2c_utils.sh" + + +# LED status monitor +function _led_monitor { + ${I2C_UTILS} i2c_led_fan_status_set >/dev/null + ${I2C_UTILS} i2c_led_psu_status_set >/dev/null + ${I2C_UTILS} i2c_led_fan_tray_status_set >/dev/null +} + +# main function +function _main { + while true + do + _led_monitor + # Sleep while still handling signals + sleep $INTERVAL & + wait $! + done +} + +_main