diff --git a/device/accton/x86_64-accton_as7312_54xs-r0/Accton-AS7312-54XS/port_config.ini b/device/accton/x86_64-accton_as7312_54xs-r0/Accton-AS7312-54XS/port_config.ini
index 18b0ed20d3..93160688d7 100755
--- a/device/accton/x86_64-accton_as7312_54xs-r0/Accton-AS7312-54XS/port_config.ini
+++ b/device/accton/x86_64-accton_as7312_54xs-r0/Accton-AS7312-54XS/port_config.ini
@@ -1,55 +1,55 @@
-# name lanes alias index
-Ethernet0 41 twentyfiveGigE1 0
-Ethernet1 42 twentyfiveGigE2 1
-Ethernet2 43 twentyfiveGigE3 2
-Ethernet3 44 twentyfiveGigE4 3
-Ethernet4 49 twentyfiveGigE5 4
-Ethernet5 50 twentyfiveGigE6 5
-Ethernet6 51 twentyfiveGigE7 6
-Ethernet7 52 twentyfiveGigE8 7
-Ethernet8 53 twentyfiveGigE9 8
-Ethernet9 54 twentyfiveGigE10 9
-Ethernet10 55 twentyfiveGigE11 10
-Ethernet11 56 twentyfiveGigE12 11
-Ethernet12 65 twentyfiveGigE13 12
-Ethernet13 66 twentyfiveGigE14 13
-Ethernet14 67 twentyfiveGigE15 14
-Ethernet15 68 twentyfiveGigE16 15
-Ethernet16 33 twentyfiveGigE17 16
-Ethernet17 34 twentyfiveGigE18 17
-Ethernet18 35 twentyfiveGigE19 18
-Ethernet19 36 twentyfiveGigE20 19
-Ethernet20 37 twentyfiveGigE21 20
-Ethernet21 38 twentyfiveGigE22 21
-Ethernet22 39 twentyfiveGigE23 22
-Ethernet23 40 twentyfiveGigE24 23
-Ethernet24 69 twentyfiveGigE25 24
-Ethernet25 70 twentyfiveGigE26 25
-Ethernet26 71 twentyfiveGigE27 26
-Ethernet27 72 twentyfiveGigE28 27
-Ethernet28 81 twentyfiveGigE29 28
-Ethernet29 82 twentyfiveGigE30 29
-Ethernet30 83 twentyfiveGigE31 30
-Ethernet31 84 twentyfiveGigE32 31
-Ethernet32 85 twentyfiveGigE33 32
-Ethernet33 86 twentyfiveGigE34 33
-Ethernet34 87 twentyfiveGigE35 34
-Ethernet35 88 twentyfiveGigE36 35
-Ethernet36 97 twentyfiveGigE37 36
-Ethernet37 98 twentyfiveGigE38 37
-Ethernet38 99 twentyfiveGigE39 38
-Ethernet39 100 twentyfiveGigE40 39
-Ethernet40 101 twentyfiveGigE41 40
-Ethernet41 102 twentyfiveGigE42 41
-Ethernet42 103 twentyfiveGigE43 42
-Ethernet43 104 twentyfiveGigE44 43
-Ethernet44 105 twentyfiveGigE45 44
-Ethernet45 106 twentyfiveGigE46 45
-Ethernet46 107 twentyfiveGigE47 46
-Ethernet47 108 twentyfiveGigE48 47
-Ethernet48 5,6,7,8 hundredGigE49 48
-Ethernet52 1,2,3,4 hundredGigE50 52
-Ethernet56 109,110,111,112 hundredGigE51 56
-Ethernet60 21,22,23,24 hundredGigE52 60
-Ethernet64 9,10,11,12 hundredGigE53 64
-Ethernet68 117,118,119,120 hundredGigE54 68
+# name lanes alias index speed
+Ethernet0 41 twentyfiveGigE1 1 25000
+Ethernet1 42 twentyfiveGigE2 2 25000
+Ethernet2 43 twentyfiveGigE3 3 25000
+Ethernet3 44 twentyfiveGigE4 4 25000
+Ethernet4 49 twentyfiveGigE5 5 25000
+Ethernet5 50 twentyfiveGigE6 6 25000
+Ethernet6 51 twentyfiveGigE7 7 25000
+Ethernet7 52 twentyfiveGigE8 8 25000
+Ethernet8 53 twentyfiveGigE9 9 25000
+Ethernet9 54 twentyfiveGigE10 10 25000
+Ethernet10 55 twentyfiveGigE11 11 25000
+Ethernet11 56 twentyfiveGigE12 12 25000
+Ethernet12 65 twentyfiveGigE13 13 25000
+Ethernet13 66 twentyfiveGigE14 14 25000
+Ethernet14 67 twentyfiveGigE15 15 25000
+Ethernet15 68 twentyfiveGigE16 16 25000
+Ethernet16 33 twentyfiveGigE17 17 25000
+Ethernet17 34 twentyfiveGigE18 18 25000
+Ethernet18 35 twentyfiveGigE19 19 25000
+Ethernet19 36 twentyfiveGigE20 20 25000
+Ethernet20 37 twentyfiveGigE21 21 25000
+Ethernet21 38 twentyfiveGigE22 22 25000
+Ethernet22 39 twentyfiveGigE23 23 25000
+Ethernet23 40 twentyfiveGigE24 24 25000
+Ethernet24 69 twentyfiveGigE25 25 25000
+Ethernet25 70 twentyfiveGigE26 26 25000
+Ethernet26 71 twentyfiveGigE27 27 25000
+Ethernet27 72 twentyfiveGigE28 28 25000
+Ethernet28 81 twentyfiveGigE29 29 25000
+Ethernet29 82 twentyfiveGigE30 30 25000
+Ethernet30 83 twentyfiveGigE31 31 25000
+Ethernet31 84 twentyfiveGigE32 32 25000
+Ethernet32 85 twentyfiveGigE33 33 25000
+Ethernet33 86 twentyfiveGigE34 34 25000
+Ethernet34 87 twentyfiveGigE35 35 25000
+Ethernet35 88 twentyfiveGigE36 36 25000
+Ethernet36 97 twentyfiveGigE37 37 25000
+Ethernet37 98 twentyfiveGigE38 38 25000
+Ethernet38 99 twentyfiveGigE39 39 25000
+Ethernet39 100 twentyfiveGigE40 40 25000
+Ethernet40 101 twentyfiveGigE41 41 25000
+Ethernet41 102 twentyfiveGigE42 42 25000
+Ethernet42 103 twentyfiveGigE43 43 25000
+Ethernet43 104 twentyfiveGigE44 44 25000
+Ethernet44 105 twentyfiveGigE45 45 25000
+Ethernet45 106 twentyfiveGigE46 46 25000
+Ethernet46 107 twentyfiveGigE47 47 25000
+Ethernet47 108 twentyfiveGigE48 48 25000
+Ethernet48 5,6,7,8 hundredGigE49 49 100000
+Ethernet52 1,2,3,4 hundredGigE50 50 100000
+Ethernet56 109,110,111,112 hundredGigE51 51 100000
+Ethernet60 21,22,23,24 hundredGigE52 52 100000
+Ethernet64 9,10,11,12 hundredGigE53 53 100000
+Ethernet68 117,118,119,120 hundredGigE54 54 100000
diff --git a/device/accton/x86_64-accton_as7312_54xs-r0/plugins/sfputil.py b/device/accton/x86_64-accton_as7312_54xs-r0/plugins/sfputil.py
index b15eee579c..a1785fde55 100644
--- a/device/accton/x86_64-accton_as7312_54xs-r0/plugins/sfputil.py
+++ b/device/accton/x86_64-accton_as7312_54xs-r0/plugins/sfputil.py
@@ -15,11 +15,11 @@ except ImportError as e:
class SfpUtil(SfpUtilBase):
"""Platform-specific SfpUtil class"""
- PORT_START = 0
- PORT_END = 71
- PORTS_IN_BLOCK = 72
- QSFP_PORT_START = 48
- QSFP_PORT_END = 72
+ PORT_START = 1
+ PORT_END = 54
+ PORTS_IN_BLOCK = 54
+ QSFP_PORT_START = 49
+ QSFP_PORT_END = 54
BASE_VAL_PATH = "/sys/class/i2c-adapter/i2c-{0}/{1}-0050/"
@@ -33,78 +33,60 @@ class SfpUtil(SfpUtilBase):
2: "6-0064",
}
_port_to_i2c_mapping = {
- 0: 18,
- 1: 19,
- 2: 20,
- 3: 21,
- 4: 22,
- 5: 23,
- 6: 24,
- 7: 25,
- 8: 26,
- 9: 27,
- 10: 28,
- 11: 29,
- 12: 30,
- 13: 31,
- 14: 32,
- 15: 33,
- 16: 34,
- 17: 35,
- 18: 36,
- 19: 37,
- 20: 38,
- 21: 39,
- 22: 40,
- 23: 41,
- 24: 42,
- 25: 43,
- 26: 44,
- 27: 45,
- 28: 46,
- 29: 47,
- 30: 48,
- 31: 49,
- 32: 50,
- 33: 51,
- 34: 52,
- 35: 53,
- 36: 54,
- 37: 55,
- 38: 56,
- 39: 57,
- 40: 58,
- 41: 59,
- 42: 60,
- 43: 61,
- 44: 62,
- 45: 63,
- 46: 64,
- 47: 65,
- 48: 66, #QSFP49
- 49: 66,
- 50: 66,
- 51: 66,
- 52: 67, #QSFP50
- 53: 67,
- 54: 67,
- 55: 67,
- 56: 68, #QSFP51
- 57: 68,
- 58: 68,
- 59: 68,
- 60: 69, #QSFP52
- 61: 69,
- 62: 69,
- 63: 69,
- 64: 70, #QSFP53
- 65: 70,
- 66: 70,
- 67: 70,
- 68: 71, #QSFP54
- 69: 71,
- 70: 71,
- 71: 71,
+ 1: 18,
+ 2: 19,
+ 3: 20,
+ 4: 21,
+ 5: 22,
+ 6: 23,
+ 7: 24,
+ 8: 25,
+ 9: 26,
+ 10: 27,
+ 11: 28,
+ 12: 29,
+ 13: 30,
+ 14: 31,
+ 15: 32,
+ 16: 33,
+ 17: 34,
+ 18: 35,
+ 19: 36,
+ 20: 37,
+ 21: 38,
+ 22: 39,
+ 23: 40,
+ 24: 41,
+ 25: 42,
+ 26: 43,
+ 27: 44,
+ 28: 45,
+ 29: 46,
+ 30: 47,
+ 31: 48,
+ 32: 49,
+ 33: 50,
+ 34: 51,
+ 35: 52,
+ 36: 53,
+ 37: 54,
+ 38: 55,
+ 39: 56,
+ 40: 57,
+ 41: 58,
+ 42: 59,
+ 43: 60,
+ 44: 61,
+ 45: 62,
+ 46: 63,
+ 47: 64,
+ 48: 65,
+ 49: 66, #QSFP49
+ 50: 67,
+ 51: 68,
+ 52: 69,
+ 53: 70,
+ 54: 71, #QSFP54
}
@property
@@ -133,30 +115,18 @@ class SfpUtil(SfpUtilBase):
def __init__(self):
eeprom_path = '/sys/bus/i2c/devices/{0}-0050/eeprom'
- for x in range(0, self.port_end+1):
+ for x in range(self.port_start, self.port_end+1):
self.port_to_eeprom_mapping[x] = eeprom_path.format(
self._port_to_i2c_mapping[x])
SfpUtilBase.__init__(self)
-
- # For port 48~51 are QSFP, here presumed they're all split to 4 lanes.
- def get_cage_num(self, port_num):
- cage_num = port_num
- if (port_num >= self.QSFP_PORT_START):
- cage_num = (port_num - self.QSFP_PORT_START)/4
- cage_num = cage_num + self.QSFP_PORT_START
-
- return cage_num
-
- # For cage 0~23 and 48~51 are at cpld2, others are at cpld3.
def get_cpld_num(self, port_num):
cpld_i = 1
- cage_num = self.get_cage_num(port_num)
- if (port_num > 23 and port_num < self.QSFP_PORT_START):
+ if (port_num > 24 and port_num < self.qsfp_port_start):
cpld_i = 2
- if (cage_num >= 52):
+ if (port_num > 52):
cpld_i = 2
return cpld_i
@@ -166,12 +136,11 @@ class SfpUtil(SfpUtilBase):
if port_num < self.port_start or port_num > self.port_end:
return False
- cage_num = self.get_cage_num(port_num)
cpld_i = self.get_cpld_num(port_num)
cpld_ps = self._cpld_mapping[cpld_i]
path = "/sys/bus/i2c/devices/{0}/module_present_{1}"
- port_ps = path.format(cpld_ps, cage_num+1)
+ port_ps = path.format(cpld_ps, port_num)
try:
val_file = open(port_ps)
@@ -250,11 +219,10 @@ class SfpUtil(SfpUtilBase):
if port_num < self.qsfp_port_start or port_num > self.qsfp_port_end:
return False
- cage_num = self.get_cage_num(port_num)
cpld_i = self.get_cpld_num(port_num)
cpld_ps = self._cpld_mapping[cpld_i]
path = "/sys/bus/i2c/devices/{0}/module_reset_{1}"
- port_ps = path.format(cpld_ps, cage_num+1)
+ port_ps = path.format(cpld_ps, port_num)
try:
reg_file = open(port_ps, 'w')
except IOError as e:
diff --git a/platform/broadcom/one-image.mk b/platform/broadcom/one-image.mk
index 897845ab82..b87c6dc147 100755
--- a/platform/broadcom/one-image.mk
+++ b/platform/broadcom/one-image.mk
@@ -29,6 +29,7 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \
$(ACCTON_AS5835_54X_PLATFORM_MODULE) \
$(ACCTON_AS9716_32D_PLATFORM_MODULE) \
$(ACCTON_AS5835_54T_PLATFORM_MODULE) \
+ $(ACCTON_AS7312_54XS_PLATFORM_MODULE) \
$(INVENTEC_D7032Q28B_PLATFORM_MODULE) \
$(INVENTEC_D7054Q28B_PLATFORM_MODULE) \
$(INVENTEC_D7264Q28B_PLATFORM_MODULE) \
diff --git a/platform/broadcom/platform-modules-accton.mk b/platform/broadcom/platform-modules-accton.mk
index af8cb0fb47..6745bb5923 100755
--- a/platform/broadcom/platform-modules-accton.mk
+++ b/platform/broadcom/platform-modules-accton.mk
@@ -15,6 +15,7 @@ ACCTON_AS5812_54X_PLATFORM_MODULE_VERSION = 1.1
ACCTON_AS5835_54X_PLATFORM_MODULE_VERSION = 1.1
ACCTON_AS9716_32D_PLATFORM_MODULE_VERSION = 1.1
ACCTON_AS5835_54T_PLATFORM_MODULE_VERSION = 1.1
+ACCTON_AS7312_54XS_PLATFORM_MODULE_VERSION = 1.1
export ACCTON_AS7712_32X_PLATFORM_MODULE_VERSION
export ACCTON_AS5712_54X_PLATFORM_MODULE_VERSION
@@ -31,6 +32,7 @@ export ACCTON_AS5812_54X_PLATFORM_MODULE_VERSION
export ACCTON_AS5835_54X_PLATFORM_MODULE_VERSION
export ACCTON_AS9716_32D_PLATFORM_MODULE_VERSION
export ACCTON_AS5835_54T_PLATFORM_MODULE_VERSION
+export ACCTON_AS7312_54XS_PLATFORM_MODULE_VERSION
ACCTON_AS7712_32X_PLATFORM_MODULE = sonic-platform-accton-as7712-32x_$(ACCTON_AS7712_32X_PLATFORM_MODULE_VERSION)_amd64.deb
$(ACCTON_AS7712_32X_PLATFORM_MODULE)_SRC_PATH = $(PLATFORM_PATH)/sonic-platform-modules-accton
@@ -54,6 +56,10 @@ ACCTON_AS7312_54X_PLATFORM_MODULE = sonic-platform-accton-as7312-54x_$(ACCTON_AS
$(ACCTON_AS7312_54X_PLATFORM_MODULE)_PLATFORM = x86_64-accton_as7312_54x-r0
$(eval $(call add_extra_package,$(ACCTON_AS7712_32X_PLATFORM_MODULE),$(ACCTON_AS7312_54X_PLATFORM_MODULE)))
+ACCTON_AS7312_54XS_PLATFORM_MODULE = sonic-platform-accton-as7312-54xs_$(ACCTON_AS7312_54XS_PLATFORM_MODULE_VERSION)_amd64.deb
+$(ACCTON_AS7312_54XS_PLATFORM_MODULE)_PLATFORM = x86_64-accton_as7312_54xs-r0
+$(eval $(call add_extra_package,$(ACCTON_AS7712_32X_PLATFORM_MODULE),$(ACCTON_AS7312_54XS_PLATFORM_MODULE)))
+
ACCTON_AS7326_56X_PLATFORM_MODULE = sonic-platform-accton-as7326-56x_$(ACCTON_AS7326_56X_PLATFORM_MODULE_VERSION)_amd64.deb
$(ACCTON_AS7326_56X_PLATFORM_MODULE)_PLATFORM = x86_64-accton_as7326_56x-r0
$(eval $(call add_extra_package,$(ACCTON_AS7712_32X_PLATFORM_MODULE),$(ACCTON_AS7326_56X_PLATFORM_MODULE)))
@@ -74,7 +80,6 @@ ACCTON_AS4630_54PE_PLATFORM_MODULE = sonic-platform-accton-as4630-54pe_$(ACCTON_
$(ACCTON_AS4630_54PE_PLATFORM_MODULE)_PLATFORM = x86_64-accton_as4630_54pe-r0
$(eval $(call add_extra_package,$(ACCTON_AS7712_32X_PLATFORM_MODULE),$(ACCTON_AS4630_54PE_PLATFORM_MODULE)))
-
ACCTON_MINIPACK_PLATFORM_MODULE = sonic-platform-accton-minipack_$(ACCTON_MINIPACK_PLATFORM_MODULE_VERSION)_amd64.deb
$(ACCTON_MINIPACK_PLATFORM_MODULE)_PLATFORM = x86_64-accton_minipack-r0
$(eval $(call add_extra_package,$(ACCTON_AS7712_32X_PLATFORM_MODULE),$(ACCTON_MINIPACK_PLATFORM_MODULE)))
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/classes/__init__.py b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/classes/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/classes/fanutil.py b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/classes/fanutil.py
new file mode 100755
index 0000000000..18802c8036
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/classes/fanutil.py
@@ -0,0 +1,256 @@
+#!/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 = {}
+ logger = logging.getLogger(__name__)
+
+#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:
+ self.logger.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:
+ self.logger.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:
+ self.logger.error('GET. unable to open file: %s', str(e))
+ return None
+
+ content = val_file.readline().rstrip()
+
+ if content == '':
+ self.logger.debug('GET. content is NULL. device_path:%s', device_path)
+ return None
+
+ try:
+ val_file.close()
+ except:
+ self.logger.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:
+ self.logger.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:
+ self.logger.debug('GET. Parameter error. node_num:%d', node_num)
+ return None
+
+ content = str(val)
+ if content == '':
+ self.logger.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:
+ self.logger.error('GET. unable to open file: %s', str(e))
+ return None
+
+ val_file.write(content)
+
+ try:
+ val_file.close()
+ except:
+ self.logger.debug('GET. unable to close file. device_path:%s', device_path)
+ return None
+
+ return True
+
+ def __init__(self, log_level=logging.INFO):
+ ch = logging.StreamHandler()
+ ch.setLevel(log_level)
+ self.logger.addHandler(ch)
+
+ 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:
+ self.logger.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:
+ self.logger.debug('GET. FAN 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-54xs/classes/thermalutil.py b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/classes/thermalutil.py
new file mode 100755
index 0000000000..ddc3521faf
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/classes/thermalutil.py
@@ -0,0 +1,128 @@
+#!/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'],
+ }
+
+ logger = logging.getLogger(__name__)
+ def __init__(self, log_level=logging.INFO):
+ ch = logging.StreamHandler()
+ ch.setLevel(log_level)
+ self.logger.addHandler(ch)
+
+ 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:
+ self.logger.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:
+ self.logger.error('GET. unable to open file: %s', str(e))
+ return None
+
+ content = val_file.readline().rstrip()
+
+ if content == '':
+ self.logger.debug('GET. content is NULL. device_path:%s', device_path)
+ return None
+
+ try:
+ val_file.close()
+ except:
+ self.logger.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-54xs/modules/Makefile b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/modules/Makefile
new file mode 100755
index 0000000000..488fdb321c
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/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-54xs/modules/accton_as7312_54x_fan.c b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/modules/accton_as7312_54x_fan.c
new file mode 100755
index 0000000000..8764ec8a31
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/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-54xs/modules/accton_as7312_54x_leds.c b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/modules/accton_as7312_54x_leds.c
new file mode 100644
index 0000000000..1d54517c62
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/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-54xs/modules/accton_as7312_54x_psu.c b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/modules/accton_as7312_54x_psu.c
new file mode 100644
index 0000000000..4646224ef9
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/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-54xs/modules/accton_as7312_54x_sfp.c b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/modules/accton_as7312_54x_sfp.c
new file mode 100644
index 0000000000..921d9f892c
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/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-54xs/modules/accton_i2c_cpld.c b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/modules/accton_i2c_cpld.c
new file mode 100644
index 0000000000..67ecd8e036
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/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-54xs/modules/ym2651y.c b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/modules/ym2651y.c
new file mode 120000
index 0000000000..f4d67640cc
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/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-54xs/service/as7312-platform-monitor.service b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/service/as7312-platform-monitor.service
new file mode 100755
index 0000000000..b79357665b
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/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-54xs/setup.py b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/setup.py
new file mode 100755
index 0000000000..d1351ab3e5
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/setup.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+import os
+import sys
+from setuptools import setup
+os.listdir
+
+setup(
+ name='as7312_54xs',
+ version='1.0',
+ description='Module to initialize Accton AS7312-54XS platforms',
+
+ packages=['as7312_54xs'],
+ package_dir={'as7312_54xs': 'as7312-54xs/classes'},
+)
+
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/utils/README b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/utils/README
new file mode 100755
index 0000000000..66f31a0304
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/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-54xs/utils/accton_as7312_monitor.py b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/utils/accton_as7312_monitor.py
new file mode 100755
index 0000000000..09bc99c75a
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/utils/accton_as7312_monitor.py
@@ -0,0 +1,223 @@
+#!/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
+ import signal
+ from tabulate import tabulate
+ from as7312_54xs.fanutil import FanUtil
+ from as7312_54xs.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'
+DUTY_MAX = 100
+
+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
+ llog = logging.getLogger("["+FUNCTION_NAME+"]")
+ _ori_temp = 0
+ _new_perc = 0
+ _ori_perc = 0
+
+ def __init__(self, log_console, log_file, log_level=logging.INFO):
+ """Needs a logger and a logger level."""
+ formatter = logging.Formatter('%(name)s %(message)s')
+ sys_handler = logging.handlers.SysLogHandler(address = '/dev/log')
+ sys_handler.setFormatter(formatter)
+ sys_handler.ident = 'common'
+ sys_handler.setLevel(log_level)
+ self.llog.addHandler(sys_handler)
+
+ if log_file:
+ fh = logging.FileHandler(log_file)
+ fh.setLevel(logging.DEBUG)
+ formatter = logging.Formatter('%(asctime)-15s %(name)s %(message)s')
+ fh.setFormatter(formatter)
+ self.llog.addHandler(fh)
+
+ if log_console:
+ console = logging.StreamHandler()
+ console.setLevel(log_level)
+ formatter = logging.Formatter('%(asctime)-15s %(name)s %(message)s')
+ console.setFormatter(formatter)
+ self.llog.addHandler(console)
+
+
+ def manage_fans(self):
+ max_duty = DUTY_MAX
+ 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:
+ self.llog.debug('SET new_perc to %d (FAN stauts is None. fan_num:%d)', max_duty, x)
+ return False
+ if fan_status is False:
+ self.llog.warning('SET new_perc to %d (FAN fault. fan_num:%d)', max_duty, x)
+ fan.set_fan_duty_cycle(max_duty)
+ return True
+
+ 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]
+ self.llog.debug('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]
+ self.llog.debug('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
+
+ self.llog.debug('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 sig_handler(signum, frame):
+ fan = FanUtil()
+ logging.critical('Cause signal %d, set fan speed max.', signum)
+ fan.set_fan_duty_cycle(DUTY_MAX)
+ sys.exit(0)
+
+def main(argv):
+ log_level = logging.INFO
+ log_console = 0
+ log_file = ""
+
+ if len(sys.argv) != 1:
+ try:
+ opts, args = getopt.getopt(argv,'hdl')
+ except getopt.GetoptError:
+ print 'Usage: %s [-d]' % 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'):
+ log_console = 1
+ elif opt in ('-l'):
+ log_file = '%s.log' % sys.argv[0]
+
+ signal.signal(signal.SIGINT, sig_handler)
+ signal.signal(signal.SIGTERM, sig_handler)
+ monitor = accton_as7312_monitor(log_console, log_file)
+
+ # 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-54xs/utils/accton_as7312_util.py b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/utils/accton_as7312_util.py
new file mode 100755
index 0000000000..9b5803b07a
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7312-54xs/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 0x51 > /sys/bus/i2c/devices/i2c-11/new_device',
+'echo ym2651 0x59 > /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 0x51 > /sys/bus/i2c/devices/i2c-11/new_device',
+'echo ym2651 0x59 > /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/debian/control b/platform/broadcom/sonic-platform-modules-accton/debian/control
index 8100f6b76e..cb78e152ff 100755
--- a/platform/broadcom/sonic-platform-modules-accton/debian/control
+++ b/platform/broadcom/sonic-platform-modules-accton/debian/control
@@ -64,3 +64,7 @@ Description: kernel modules for platform devices such as fan, led, sfp
Package: sonic-platform-accton-as5835-54t
Architecture: amd64
Description: kernel modules for platform devices such as fan, led, sfp
+
+Package: sonic-platform-accton-as7312-54xs
+Architecture: amd64
+Description: kernel modules for platform devices such as fan, led, sfp
diff --git a/platform/broadcom/sonic-platform-modules-accton/debian/rules b/platform/broadcom/sonic-platform-modules-accton/debian/rules
index ba2f3e394d..e2c6a4df8f 100755
--- a/platform/broadcom/sonic-platform-modules-accton/debian/rules
+++ b/platform/broadcom/sonic-platform-modules-accton/debian/rules
@@ -19,11 +19,12 @@ PACKAGE_PRE_NAME := sonic-platform-accton
KVERSION ?= $(shell uname -r)
KERNEL_SRC := /lib/modules/$(KVERSION)
MOD_SRC_DIR:= $(shell pwd)
-MODULE_DIRS:= as7712-32x as5712-54x as7816-64x as7716-32x as7716-32xb as7312-54x as7326-56x as6712-32x as7726-32x as4630-54pe minipack as5812-54x as5835-54x as9716-32d as5835-54t
+MODULE_DIRS := as7712-32x as5712-54x as7816-64x as7716-32x as7716-32xb as7312-54x
+MODULE_DIRS += as7326-56x as6712-32x as7726-32x as4630-54pe minipack as5812-54x
+MODULE_DIRS += as5835-54x as9716-32d as5835-54t as7312-54xs
MODULE_DIR := modules
UTILS_DIR := utils
SERVICE_DIR := service
-CLASSES_DIR := classes
CONF_DIR := conf
%: