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<