diff --git a/device/accton/x86_64-accton_as7315_27xb-r0/Accton-AS7315-27XB/port_config.ini b/device/accton/x86_64-accton_as7315_27xb-r0/Accton-AS7315-27XB/port_config.ini
new file mode 100644
index 0000000000..26d49cdde5
--- /dev/null
+++ b/device/accton/x86_64-accton_as7315_27xb-r0/Accton-AS7315-27XB/port_config.ini
@@ -0,0 +1,28 @@
+# name lanes alias index speed
+Ethernet0 28 twentyfiveGigE1 1 25000
+Ethernet1 29 twentyfiveGigE2 2 25000
+Ethernet2 30 twentyfiveGigE3 3 25000
+Ethernet3 31 twentyfiveGigE4 4 25000
+Ethernet4 24 twentyfiveGigE5 5 10000
+Ethernet5 25 twentyfiveGigE6 6 10000
+Ethernet6 26 twentyfiveGigE7 7 10000
+Ethernet7 27 twentyfiveGigE8 8 10000
+Ethernet8 20 twentyfiveGigE9 9 10000
+Ethernet9 21 twentyfiveGigE10 10 10000
+Ethernet10 22 twentyfiveGigE11 11 10000
+Ethernet11 23 twentyfiveGigE12 12 10000
+Ethernet12 16 twentyfiveGigE13 13 10000
+Ethernet13 17 twentyfiveGigE14 14 10000
+Ethernet14 18 twentyfiveGigE15 15 10000
+Ethernet15 19 twentyfiveGigE16 16 10000
+Ethernet16 4 twentyfiveGigE17 17 10000
+Ethernet17 5 twentyfiveGigE18 18 10000
+Ethernet18 6 twentyfiveGigE19 19 10000
+Ethernet19 7 twentyfiveGigE20 20 10000
+Ethernet20 0 twentyfiveGigE21 21 10000
+Ethernet21 1 twentyfiveGigE22 22 10000
+Ethernet22 2 twentyfiveGigE23 23 10000
+Ethernet23 3 twentyfiveGigE24 24 10000
+Ethernet24 67,68,69,70 twentyfiveGigE25 25 100000
+Ethernet25 71,72,73,74 twentyfiveGigE26 26 100000
+Ethernet26 75,76,77,78 twentyfiveGigE27 27 100000
diff --git a/device/accton/x86_64-accton_as7315_27xb-r0/Accton-AS7315-27XB/qax-as7315-20x10G+4x25G+3x100G.config.bcm b/device/accton/x86_64-accton_as7315_27xb-r0/Accton-AS7315-27XB/qax-as7315-20x10G+4x25G+3x100G.config.bcm
new file mode 100644
index 0000000000..eef4d987e8
--- /dev/null
+++ b/device/accton/x86_64-accton_as7315_27xb-r0/Accton-AS7315-27XB/qax-as7315-20x10G+4x25G+3x100G.config.bcm
@@ -0,0 +1,519 @@
+# accton_as7312_54x 48x25G+6x100G SDK config
+os=unix
+schan_intr_enable=0
+
+l2_mem_entries=40960
+l2xmsg_mode=1
+l3_mem_entries=40960
+mem_cache_enable=0
+parity_correction=0
+parity_enable=0
+mmu_lossless=1
+
+pbmp_oversubscribe=0x0407ffc00ff00ff003fc3ffc00200222
+pbmp_xport_xe=0x0407ffd00ff00ff403fc3ffc00200222
+
+## FC10 ##
+dport_map_port_42=1
+dport_map_port_43=2
+dport_map_port_44=3
+dport_map_port_45=4
+
+## FC12 ##
+dport_map_port_50=5
+dport_map_port_51=6
+dport_map_port_52=7
+dport_map_port_53=8
+
+## FC13 ##
+dport_map_port_54=9
+dport_map_port_55=10
+dport_map_port_56=11
+dport_map_port_57=12
+
+## FC16 ##
+dport_map_port_68=13
+dport_map_port_69=14
+dport_map_port_70=15
+dport_map_port_71=16
+
+## FC8 ##
+dport_map_port_34=17
+dport_map_port_35=18
+dport_map_port_36=19
+dport_map_port_37=20
+
+## FC9 ##
+dport_map_port_38=21
+dport_map_port_39=22
+dport_map_port_40=23
+dport_map_port_41=24
+
+## FC17 ##
+dport_map_port_72=25
+dport_map_port_73=26
+dport_map_port_74=27
+dport_map_port_75=28
+
+## FC20 ##
+dport_map_port_84=29
+dport_map_port_85=30
+dport_map_port_86=31
+dport_map_port_87=32
+
+## FC21 ##
+dport_map_port_88=33
+dport_map_port_89=34
+dport_map_port_90=35
+dport_map_port_91=36
+
+## FC24 ##
+dport_map_port_102=37
+dport_map_port_103=38
+dport_map_port_104=39
+dport_map_port_105=40
+
+## FC25 ##
+dport_map_port_106=41
+dport_map_port_107=42
+dport_map_port_108=43
+dport_map_port_109=44
+
+## FC26 ##
+dport_map_port_110=45
+dport_map_port_111=46
+dport_map_port_112=47
+dport_map_port_113=48
+
+## FC1 ##
+dport_map_port_5=49
+
+## FC0 ##
+dport_map_port_1=50
+
+## FC27 ##
+dport_map_port_114=51
+
+## FC5 ##
+dport_map_port_21=52
+
+## FC2 ##
+dport_map_port_9=53
+
+## FC29 ##
+dport_map_port_122=54
+
+#for KR
+#dport_map_port_66=55
+#dport_map_port_100=56
+
+/* Port Map */
+## FC10 ##
+portmap_42=41:25
+portmap_43=42:25
+portmap_44=43:25
+portmap_45=44:25
+
+## FC12 ##
+portmap_50=49:25
+portmap_51=50:25
+portmap_52=51:25
+portmap_53=52:25
+
+
+## FC13 ##
+portmap_54=53:25
+portmap_55=54:25
+portmap_56=55:25
+portmap_57=56:25
+
+## FC16 ##
+portmap_68=65:25
+portmap_69=66:25
+portmap_70=67:25
+portmap_71=68:25
+
+
+## FC8 ##
+portmap_34=33:25
+portmap_35=34:25
+portmap_36=35:25
+portmap_37=36:25
+
+## FC9 ##
+portmap_38=37:25
+portmap_39=38:25
+portmap_40=39:25
+portmap_41=40:25
+
+## FC17 ##
+portmap_72=69:25
+portmap_73=70:25
+portmap_74=71:25
+portmap_75=72:25
+
+## FC20 ##
+portmap_84=81:25
+portmap_85=82:25
+portmap_86=83:25
+portmap_87=84:25
+
+
+## FC21 ##
+portmap_88=85:25
+portmap_89=86:25
+portmap_90=87:25
+portmap_91=88:25
+
+## FC24 ##
+portmap_102=97:25
+portmap_103=98:25
+portmap_104=99:25
+portmap_105=100:25
+
+## FC25 ##
+portmap_106=101:25
+portmap_107=102:25
+portmap_108=103:25
+portmap_109=104:25
+
+## FC26 ##
+portmap_110=105:25
+portmap_111=106:25
+portmap_112=107:25
+portmap_113=108:25
+
+## FC1 ##
+portmap_5=5:100
+
+## FC0 ##
+portmap_1=1:100
+
+## FC27 ##
+portmap_114=109:100
+
+## FC5 ##
+portmap_21=21:100
+
+## FC2 ##
+portmap_9=9:100
+
+## FC29 ##
+portmap_122=117:100
+
+# CPU to MAC
+# TSC-E management port 1
+#portmap_66=129:10
+# TSC-E management port 2
+#portmap_100=131:10
+
+xgxs_rx_lane_map_42=0x2310
+xgxs_rx_lane_map_43=0x2310
+xgxs_rx_lane_map_44=0x2310
+xgxs_rx_lane_map_45=0x2310
+xgxs_rx_lane_map_50=0x3210
+xgxs_rx_lane_map_51=0x3210
+xgxs_rx_lane_map_52=0x3210
+xgxs_rx_lane_map_53=0x3210
+xgxs_rx_lane_map_54=0x3210
+xgxs_rx_lane_map_55=0x3210
+xgxs_rx_lane_map_56=0x3210
+xgxs_rx_lane_map_57=0x3210
+xgxs_rx_lane_map_68=0x0123
+xgxs_rx_lane_map_69=0x0123
+xgxs_rx_lane_map_70=0x0123
+xgxs_rx_lane_map_71=0x0123
+xgxs_rx_lane_map_34=0x0123
+xgxs_rx_lane_map_35=0x0123
+xgxs_rx_lane_map_36=0x0123
+xgxs_rx_lane_map_37=0x0123
+xgxs_rx_lane_map_38=0x0123
+xgxs_rx_lane_map_39=0x0123
+xgxs_rx_lane_map_40=0x0123
+xgxs_rx_lane_map_41=0x0123
+xgxs_rx_lane_map_72=0x3210
+xgxs_rx_lane_map_73=0x3210
+xgxs_rx_lane_map_74=0x3210
+xgxs_rx_lane_map_75=0x3210
+xgxs_rx_lane_map_84=0x1032
+xgxs_rx_lane_map_85=0x1032
+xgxs_rx_lane_map_86=0x1032
+xgxs_rx_lane_map_87=0x1032
+xgxs_rx_lane_map_88=0x2301
+xgxs_rx_lane_map_89=0x2301
+xgxs_rx_lane_map_90=0x2301
+xgxs_rx_lane_map_91=0x2301
+xgxs_rx_lane_map_102=0x0123
+xgxs_rx_lane_map_103=0x0123
+xgxs_rx_lane_map_104=0x0123
+xgxs_rx_lane_map_105=0x0123
+xgxs_rx_lane_map_106=0x3210
+xgxs_rx_lane_map_107=0x3210
+xgxs_rx_lane_map_108=0x3210
+xgxs_rx_lane_map_109=0x3210
+xgxs_rx_lane_map_110=0x1032
+xgxs_rx_lane_map_111=0x1032
+xgxs_rx_lane_map_112=0x1032
+xgxs_rx_lane_map_113=0x1032
+xgxs_rx_lane_map_5=0x3210
+xgxs_rx_lane_map_1=0x3210
+xgxs_rx_lane_map_114=0x0123
+xgxs_rx_lane_map_21=0x0213
+xgxs_rx_lane_map_9=0x3210
+xgxs_rx_lane_map_122=0x1230
+
+xgxs_tx_lane_map_42=0x0132
+xgxs_tx_lane_map_43=0x0132
+xgxs_tx_lane_map_44=0x0132
+xgxs_tx_lane_map_45=0x0132
+xgxs_tx_lane_map_50=0x3210
+xgxs_tx_lane_map_51=0x3210
+xgxs_tx_lane_map_52=0x3210
+xgxs_tx_lane_map_53=0x3210
+xgxs_tx_lane_map_54=0x3210
+xgxs_tx_lane_map_55=0x3210
+xgxs_tx_lane_map_56=0x3210
+xgxs_tx_lane_map_57=0x3210
+xgxs_tx_lane_map_68=0x0123
+xgxs_tx_lane_map_69=0x0123
+xgxs_tx_lane_map_70=0x0123
+xgxs_tx_lane_map_71=0x0123
+xgxs_tx_lane_map_34=0x0123
+xgxs_tx_lane_map_35=0x0123
+xgxs_tx_lane_map_36=0x0123
+xgxs_tx_lane_map_37=0x0123
+xgxs_tx_lane_map_38=0x0123
+xgxs_tx_lane_map_39=0x0123
+xgxs_tx_lane_map_40=0x0123
+xgxs_tx_lane_map_41=0x0123
+xgxs_tx_lane_map_72=0x0123
+xgxs_tx_lane_map_73=0x0123
+xgxs_tx_lane_map_74=0x0123
+xgxs_tx_lane_map_75=0x0123
+xgxs_tx_lane_map_84=0x0123
+xgxs_tx_lane_map_85=0x0123
+xgxs_tx_lane_map_86=0x0123
+xgxs_tx_lane_map_87=0x0123
+xgxs_tx_lane_map_88=0x2301
+xgxs_tx_lane_map_89=0x2301
+xgxs_tx_lane_map_90=0x2301
+xgxs_tx_lane_map_91=0x2301
+xgxs_tx_lane_map_102=0x0123
+xgxs_tx_lane_map_103=0x0123
+xgxs_tx_lane_map_104=0x0123
+xgxs_tx_lane_map_105=0x0123
+xgxs_tx_lane_map_106=0x3210
+xgxs_tx_lane_map_107=0x3210
+xgxs_tx_lane_map_108=0x3210
+xgxs_tx_lane_map_109=0x3210
+xgxs_tx_lane_map_110=0x1032
+xgxs_tx_lane_map_111=0x1032
+xgxs_tx_lane_map_112=0x1032
+xgxs_tx_lane_map_113=0x1032
+xgxs_tx_lane_map_5=0x3210
+xgxs_tx_lane_map_1=0x3210
+xgxs_tx_lane_map_114=0x0123
+xgxs_tx_lane_map_21=0x3210
+xgxs_tx_lane_map_9=0x3210
+xgxs_tx_lane_map_122=0x3210
+
+#Polarity RX
+phy_xaui_rx_polarity_flip_34=0x1
+phy_xaui_rx_polarity_flip_35=0x1
+phy_xaui_rx_polarity_flip_36=0x1
+phy_xaui_rx_polarity_flip_37=0x1
+phy_xaui_rx_polarity_flip_38=0x1
+phy_xaui_rx_polarity_flip_39=0x1
+phy_xaui_rx_polarity_flip_40=0x1
+phy_xaui_rx_polarity_flip_41=0x1
+phy_xaui_rx_polarity_flip_84=0x1
+phy_xaui_rx_polarity_flip_85=0x0
+phy_xaui_rx_polarity_flip_86=0x1
+phy_xaui_rx_polarity_flip_87=0x0
+phy_xaui_rx_polarity_flip_88=0x1
+phy_xaui_rx_polarity_flip_89=0x0
+phy_xaui_rx_polarity_flip_90=0x1
+phy_xaui_rx_polarity_flip_91=0x1
+phy_xaui_rx_polarity_flip_102=0x0
+phy_xaui_rx_polarity_flip_103=0x0
+phy_xaui_rx_polarity_flip_104=0x1
+phy_xaui_rx_polarity_flip_105=0x0
+phy_xaui_rx_polarity_flip_122=0xf
+#Polarity TX
+phy_xaui_tx_polarity_flip_42=0x1
+phy_xaui_tx_polarity_flip_43=0x1
+phy_xaui_tx_polarity_flip_44=0x1
+phy_xaui_tx_polarity_flip_45=0x1
+phy_xaui_tx_polarity_flip_34=0x1
+phy_xaui_tx_polarity_flip_35=0x1
+phy_xaui_tx_polarity_flip_36=0x1
+phy_xaui_tx_polarity_flip_37=0x1
+phy_xaui_tx_polarity_flip_38=0x0
+phy_xaui_tx_polarity_flip_39=0x1
+phy_xaui_tx_polarity_flip_40=0x0
+phy_xaui_tx_polarity_flip_41=0x1
+phy_xaui_tx_polarity_flip_72=0x1
+phy_xaui_tx_polarity_flip_73=0x1
+phy_xaui_tx_polarity_flip_74=0x1
+phy_xaui_tx_polarity_flip_75=0x1
+phy_xaui_tx_polarity_flip_84=0x1
+phy_xaui_tx_polarity_flip_85=0x1
+phy_xaui_tx_polarity_flip_86=0x1
+phy_xaui_tx_polarity_flip_87=0x1
+phy_xaui_tx_polarity_flip_88=0x1
+phy_xaui_tx_polarity_flip_89=0x1
+phy_xaui_tx_polarity_flip_90=0x1
+phy_xaui_tx_polarity_flip_91=0x1
+phy_xaui_tx_polarity_flip_102=0x1
+phy_xaui_tx_polarity_flip_103=0x1
+phy_xaui_tx_polarity_flip_104=0x1
+phy_xaui_tx_polarity_flip_105=0x1
+phy_xaui_tx_polarity_flip_122=0xb
+
+#Driver Current
+serdes_driver_current_42=0x8
+serdes_driver_current_43=0x8
+serdes_driver_current_44=0x8
+serdes_driver_current_45=0x8
+serdes_driver_current_50=0x8
+serdes_driver_current_51=0x8
+serdes_driver_current_52=0x8
+serdes_driver_current_53=0x8
+serdes_driver_current_54=0x8
+serdes_driver_current_55=0x8
+serdes_driver_current_56=0x8
+serdes_driver_current_57=0x8
+serdes_driver_current_68=0x8
+serdes_driver_current_69=0x8
+serdes_driver_current_70=0x8
+serdes_driver_current_71=0x8
+serdes_driver_current_34=0x8
+serdes_driver_current_35=0x8
+serdes_driver_current_36=0x8
+serdes_driver_current_37=0x8
+serdes_driver_current_38=0x8
+serdes_driver_current_39=0x8
+serdes_driver_current_40=0x8
+serdes_driver_current_41=0x8
+serdes_driver_current_72=0x8
+serdes_driver_current_73=0x8
+serdes_driver_current_74=0x8
+serdes_driver_current_75=0x8
+serdes_driver_current_84=0x8
+serdes_driver_current_85=0x8
+serdes_driver_current_86=0x8
+serdes_driver_current_87=0x8
+serdes_driver_current_88=0x8
+serdes_driver_current_89=0x8
+serdes_driver_current_90=0x8
+serdes_driver_current_91=0x8
+serdes_driver_current_102=0x8
+serdes_driver_current_103=0x8
+serdes_driver_current_104=0x8
+serdes_driver_current_105=0x8
+serdes_driver_current_106=0x8
+serdes_driver_current_107=0x8
+serdes_driver_current_108=0x8
+serdes_driver_current_109=0x8
+serdes_driver_current_110=0x8
+serdes_driver_current_111=0x8
+serdes_driver_current_112=0x8
+serdes_driver_current_113=0x8
+serdes_driver_current_lane0_5=0x8
+serdes_driver_current_lane1_5=0x8
+serdes_driver_current_lane2_5=0x8
+serdes_driver_current_lane3_5=0x8
+serdes_driver_current_lane0_1=0x8
+serdes_driver_current_lane1_1=0x8
+serdes_driver_current_lane2_1=0x8
+serdes_driver_current_lane3_1=0x8
+serdes_driver_current_lane0_114=0x8
+serdes_driver_current_lane1_114=0x8
+serdes_driver_current_lane2_114=0x8
+serdes_driver_current_lane3_114=0x8
+serdes_driver_current_lane0_21=0x8
+serdes_driver_current_lane1_21=0x8
+serdes_driver_current_lane2_21=0x8
+serdes_driver_current_lane3_21=0x8
+serdes_driver_current_lane0_9=0x8
+serdes_driver_current_lane1_9=0x8
+serdes_driver_current_lane2_9=0x8
+serdes_driver_current_lane3_9=0x8
+serdes_driver_current_lane0_122=0x8
+serdes_driver_current_lane1_122=0x8
+serdes_driver_current_lane2_122=0x8
+serdes_driver_current_lane3_122=0x8
+
+#Preemphasis
+serdes_preemphasis_42=0x264006
+serdes_preemphasis_43=0x264006
+serdes_preemphasis_44=0x254106
+serdes_preemphasis_45=0x254106
+serdes_preemphasis_50=0x254106
+serdes_preemphasis_51=0x254106
+serdes_preemphasis_52=0x254106
+serdes_preemphasis_53=0x254106
+serdes_preemphasis_54=0x254106
+serdes_preemphasis_55=0x254106
+serdes_preemphasis_56=0x254106
+serdes_preemphasis_57=0x234306
+serdes_preemphasis_68=0x234306
+serdes_preemphasis_69=0x204606
+serdes_preemphasis_70=0x204606
+serdes_preemphasis_71=0x204606
+serdes_preemphasis_34=0x234306
+serdes_preemphasis_35=0x234306
+serdes_preemphasis_36=0x234306
+serdes_preemphasis_37=0x234306
+serdes_preemphasis_38=0x234306
+serdes_preemphasis_39=0x234306
+serdes_preemphasis_40=0x234306
+serdes_preemphasis_41=0x234306
+serdes_preemphasis_72=0x1e4806
+serdes_preemphasis_73=0x1e4806
+serdes_preemphasis_74=0x1e4806
+serdes_preemphasis_75=0x1e4806
+serdes_preemphasis_84=0x1e4806
+serdes_preemphasis_85=0x1a4c06
+serdes_preemphasis_86=0x1a4c06
+serdes_preemphasis_87=0x1b4b06
+serdes_preemphasis_88=0x1b4b06
+serdes_preemphasis_89=0x1e4806
+serdes_preemphasis_90=0x1e4806
+serdes_preemphasis_91=0x1e4806
+serdes_preemphasis_102=0x1e4806
+serdes_preemphasis_103=0x1e4806
+serdes_preemphasis_104=0x1e4806
+serdes_preemphasis_105=0x1e4806
+serdes_preemphasis_106=0x1e4806
+serdes_preemphasis_107=0x1e4806
+serdes_preemphasis_108=0x1e4806
+serdes_preemphasis_109=0x1e4806
+serdes_preemphasis_110=0x1e4806
+serdes_preemphasis_111=0x1d4906
+serdes_preemphasis_112=0x234306
+serdes_preemphasis_113=0x1f4706
+serdes_preemphasis_lane0_5=0x294106
+serdes_preemphasis_lane1_5=0x294106
+serdes_preemphasis_lane2_5=0x294106
+serdes_preemphasis_lane3_5=0x294106
+serdes_preemphasis_lane0_1=0x294106
+serdes_preemphasis_lane1_1=0x294106
+serdes_preemphasis_lane2_1=0x294106
+serdes_preemphasis_lane3_1=0x294106
+serdes_preemphasis_lane0_114=0x2a4006
+serdes_preemphasis_lane1_114=0x2a4006
+serdes_preemphasis_lane2_114=0x2a4006
+serdes_preemphasis_lane3_114=0x2a4006
+serdes_preemphasis_lane0_21=0x2c3c08
+serdes_preemphasis_lane1_21=0x2a4006
+serdes_preemphasis_lane2_21=0x2a4006
+serdes_preemphasis_lane3_21=0x2a4006
+serdes_preemphasis_lane0_9=0x284206
+serdes_preemphasis_lane1_9=0x284206
+serdes_preemphasis_lane2_9=0x284206
+serdes_preemphasis_lane3_9=0x284206
+serdes_preemphasis_lane0_122=0x283e06
+serdes_preemphasis_lane1_122=0x283e06
+serdes_preemphasis_lane2_122=0x283e06
+serdes_preemphasis_lane3_122=0x294601
diff --git a/device/accton/x86_64-accton_as7315_27xb-r0/Accton-AS7315-27XB/sai.profile b/device/accton/x86_64-accton_as7315_27xb-r0/Accton-AS7315-27XB/sai.profile
new file mode 100644
index 0000000000..191b27dac2
--- /dev/null
+++ b/device/accton/x86_64-accton_as7315_27xb-r0/Accton-AS7315-27XB/sai.profile
@@ -0,0 +1 @@
+SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/qax-as7315-20x10G+4x25G+3x100G.config.bcm
diff --git a/device/accton/x86_64-accton_as7315_27xb-r0/default_sku b/device/accton/x86_64-accton_as7315_27xb-r0/default_sku
new file mode 100644
index 0000000000..5c1bb0b4a4
--- /dev/null
+++ b/device/accton/x86_64-accton_as7315_27xb-r0/default_sku
@@ -0,0 +1 @@
+Accton-AS7315-27XB t1
diff --git a/device/accton/x86_64-accton_as7315_27xb-r0/installer.conf b/device/accton/x86_64-accton_as7315_27xb-r0/installer.conf
new file mode 100644
index 0000000000..9fa12f8885
--- /dev/null
+++ b/device/accton/x86_64-accton_as7315_27xb-r0/installer.conf
@@ -0,0 +1,2 @@
+CONSOLE_SPEED=115200
+ONIE_PLATFORM_EXTRA_CMDLINE_LINUX="tg3.short_preamble=1 tg3.bcm5718s_reset=1"
diff --git a/device/accton/x86_64-accton_as7315_27xb-r0/led_proc_init.soc b/device/accton/x86_64-accton_as7315_27xb-r0/led_proc_init.soc
new file mode 100755
index 0000000000..790ab9cfc9
--- /dev/null
+++ b/device/accton/x86_64-accton_as7315_27xb-r0/led_proc_init.soc
@@ -0,0 +1,24 @@
+# accton_as7315_27xb LED macro init SOC
+#########################################
+## LED program for BCM88470 Qumran-AX
+#########################################
+led 0 stop
+led 0 prog \
+ 02 FC 42 03 02 F9 42 00 02 00 67 26 67 2D 86 F9 \
+ 67 2D 86 F9 67 2D 86 F9 67 2D 86 F9 06 F9 D2 24 \
+ 74 0A 86 FB 3A 7E 67 59 67 41 67 69 57 67 59 67 \
+ 41 67 72 1A 00 75 7B 1A 01 75 3D 77 50 67 59 77 \
+ 46 12 A0 F8 15 57 80 28 32 00 32 01 B7 97 75 7F \
+ 16 FB 06 FC C8 70 7F 77 7B 06 F9 C2 FC 98 98 12 \
+ E0 F8 05 16 F9 CA 03 F1 57 1A 07 27 87 1A 06 27 \
+ 87 57 1A 05 27 87 1A 04 27 87 57 32 0E 87 57 32 \
+ 0F 87 57 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+led 0 start
+led auto on
diff --git a/device/accton/x86_64-accton_as7315_27xb-r0/plugins/eeprom.py b/device/accton/x86_64-accton_as7315_27xb-r0/plugins/eeprom.py
new file mode 100644
index 0000000000..171593068a
--- /dev/null
+++ b/device/accton/x86_64-accton_as7315_27xb-r0/plugins/eeprom.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+try:
+ import exceptions
+ import binascii
+ import time
+ import optparse
+ import warnings
+ import os
+ import sys
+ from sonic_eeprom import eeprom_base
+ from sonic_eeprom import eeprom_tlvinfo
+ import subprocess
+except ImportError, e:
+ raise ImportError (str(e) + "- required module not found")
+
+class board(eeprom_tlvinfo.TlvInfoDecoder):
+ _TLV_INFO_MAX_LEN = 256
+ def __init__(self, name, path, cpld_root, ro):
+ self.eeprom_path = "/sys/bus/i2c/devices/4-0057/eeprom"
+ super(board, self).__init__(self.eeprom_path, 0, '', True)
diff --git a/device/accton/x86_64-accton_as7315_27xb-r0/plugins/psuutil.py b/device/accton/x86_64-accton_as7315_27xb-r0/plugins/psuutil.py
new file mode 100644
index 0000000000..7ec16640e5
--- /dev/null
+++ b/device/accton/x86_64-accton_as7315_27xb-r0/plugins/psuutil.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+
+#############################################################################
+# Accton
+#
+# Module contains an implementation of SONiC PSU Base API and
+# provides the PSUs status which are available in the platform
+#
+#############################################################################
+
+import os.path
+
+try:
+ from sonic_psu.psu_base import PsuBase
+except ImportError as e:
+ raise ImportError (str(e) + "- required module not found")
+
+class PsuUtil(PsuBase):
+ """Platform-specific PSUutil class"""
+
+ def __init__(self):
+ PsuBase.__init__(self)
+
+ self.psu_path = "/sys/bus/i2c/devices/"
+ self.psu_presence = "/psu_present"
+ self.psu_oper_status = "/psu_power_good"
+ self.psu_mapping = {
+ 1: "13-0053",
+ 2: "12-0050",
+ }
+
+ def get_num_psus(self):
+ return len(self.psu_mapping)
+
+ def get_psu_status(self, index):
+ if index is None:
+ return False
+
+ status = 0
+ node = self.psu_path + self.psu_mapping[index]+self.psu_oper_status
+ try:
+ with open(node, 'r') as power_status:
+ status = int(power_status.read())
+ except IOError:
+ return False
+
+ return status == 1
+
+ def get_psu_presence(self, index):
+ if index is None:
+ return False
+
+ status = 0
+ node = self.psu_path + self.psu_mapping[index] + self.psu_presence
+ try:
+ with open(node, 'r') as presence_status:
+ status = int(presence_status.read())
+ except IOError:
+ return False
+
+ return status == 1
diff --git a/device/accton/x86_64-accton_as7315_27xb-r0/plugins/sfputil.py b/device/accton/x86_64-accton_as7315_27xb-r0/plugins/sfputil.py
new file mode 100644
index 0000000000..1d777d25ae
--- /dev/null
+++ b/device/accton/x86_64-accton_as7315_27xb-r0/plugins/sfputil.py
@@ -0,0 +1,254 @@
+# sfputil.py
+#
+# Platform-specific SFP transceiver interface for SONiC
+#
+
+try:
+ import time
+ import string
+ import pprint
+ from ctypes import create_string_buffer
+ from sonic_sfp.sfputilbase import SfpUtilBase
+except ImportError as e:
+ raise ImportError("%s - required module not found" % str(e))
+
+#from xcvrd
+SFP_STATUS_REMOVED = '0'
+SFP_STATUS_INSERTED = '1'
+
+class SfpUtil(SfpUtilBase):
+ """Platform-specific SfpUtil class"""
+
+ PORT_START = 1
+ PORT_END = 27
+ PORTS_IN_BLOCK = 27
+ QSFP_PORT_START = 25
+ QSFP_PORT_END = 27
+
+ BASE_VAL_PATH = "/sys/class/i2c-adapter/i2c-{0}/{1}-0050/"
+
+ _port_to_is_present = {}
+ _port_to_lp_mode = {}
+
+ _port_to_eeprom_mapping = {}
+ _cpld_mapping = [ "8-0063", "7-0064"]
+
+ _port_to_i2c_mapping = {
+ 1: 26,
+ 2: 27,
+ 3: 28,
+ 4: 29,
+ 5: 30,
+ 6: 31,
+ 7: 32,
+ 8: 33,
+ 9: 34,
+ 10: 35,
+ 11: 36,
+ 12: 37,
+ 13: 38,
+ 14: 39,
+ 15: 40,
+ 16: 41,
+ 17: 42,
+ 18: 43,
+ 19: 44,
+ 20: 45,
+ 21: 46,
+ 22: 47,
+ 23: 48,
+ 24: 49,
+ 25: 21, #QSFP
+ 26: 22,
+ 27: 23,
+ }
+
+ @property
+ def port_start(self):
+ return self.PORT_START
+
+ @property
+ def port_end(self):
+ return self.PORT_END
+
+ @property
+ def qsfp_port_start(self):
+ return self.QSFP_PORT_START
+
+ @property
+ def qsfp_port_end(self):
+ return self.QSFP_PORT_END
+
+ @property
+ def qsfp_ports(self):
+ return range(self.QSFP_PORT_START, self.PORTS_IN_BLOCK + 1)
+
+ @property
+ def port_to_eeprom_mapping(self):
+ return self._port_to_eeprom_mapping
+
+ def __init__(self):
+ eeprom_path = '/sys/bus/i2c/devices/{0}-0050/eeprom'
+ 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])
+
+ self.get_transceiver_change_event()
+ SfpUtilBase.__init__(self)
+
+ def get_cpld_num(self, port_num):
+ cpld_i = 0
+ if (port_num >= self.qsfp_port_start):
+ cpld_i = 1
+
+ return cpld_i
+
+ def get_presence(self, port_num):
+ # Check for invalid port_num
+ if port_num < self.port_start or port_num > self.port_end:
+ return False
+
+ cpld_i = self.get_cpld_num(port_num)
+ cpld_ps = self._cpld_mapping[cpld_i]
+ path = "/sys/bus/i2c/devices/{0}/present_{1}"
+ index = ((port_num-1)%24) +1
+ port_ps = path.format(cpld_ps, index)
+
+ try:
+ val_file = open(port_ps)
+ except IOError as e:
+ print "Error: unable to open file: %s" % str(e)
+ return False
+
+ content = val_file.readline().rstrip()
+ val_file.close()
+
+ # content is a string, either "0" or "1"
+ if content == "1":
+ return True
+
+ return False
+
+ def get_low_power_mode(self, port_num):
+ # Check for invalid port_num
+ if port_num < self.qsfp_port_start or port_num > self.qsfp_port_end:
+ return False
+
+ try:
+ eeprom = None
+
+ if not self.get_presence(port_num):
+ return False
+
+ eeprom = open(self.port_to_eeprom_mapping[port_num], "rb")
+ eeprom.seek(93)
+ lpmode = ord(eeprom.read(1))
+
+ if ((lpmode & 0x3) == 0x3):
+ return True # Low Power Mode if "Power override" bit is 1 and "Power set" bit is 1
+ else:
+ return False # High Power Mode if one of the following conditions is matched:
+ # 1. "Power override" bit is 0
+ # 2. "Power override" bit is 1 and "Power set" bit is 0
+ except IOError as e:
+ print "Error: unable to open file: %s" % str(e)
+ return False
+ finally:
+ if eeprom is not None:
+ eeprom.close()
+ time.sleep(0.01)
+
+ def set_low_power_mode(self, port_num, lpmode):
+ # Check for invalid port_num
+ if port_num < self.qsfp_port_start or port_num > self.qsfp_port_end:
+ return False
+
+ try:
+ eeprom = None
+
+ if not self.get_presence(port_num):
+ return False # Port is not present, unable to set the eeprom
+
+ # Fill in write buffer
+ regval = 0x3 if lpmode else 0x1 # 0x3:Low Power Mode, 0x1:High Power Mode
+ buffer = create_string_buffer(1)
+ buffer[0] = chr(regval)
+
+ # Write to eeprom
+ eeprom = open(self.port_to_eeprom_mapping[port_num], "r+b")
+ eeprom.seek(93)
+ eeprom.write(buffer[0])
+ return True
+ except IOError as e:
+ print "Error: unable to open file: %s" % str(e)
+ return False
+ finally:
+ if eeprom is not None:
+ eeprom.close()
+ time.sleep(0.01)
+
+ def reset(self, port_num):
+ raise NotImplementedError
+
+ @property
+ def _get_present_bitmap(self):
+ nodes = []
+ port_num = [24,3]
+
+ path = "/sys/bus/i2c/devices/{0}/"
+ cpld_path = path.format(self._cpld_mapping[0])
+ nodes.append((cpld_path + "module_present_all", port_num[0]))
+ cpld_path = path.format(self._cpld_mapping[1])
+ nodes.append((cpld_path + "module_present_all", port_num[1]))
+
+ bitmap = []
+ for node in nodes:
+ try:
+ reg_file = open(node[0])
+
+ except IOError as e:
+ print "Error: unable to open file: %s" % str(e)
+ return False
+ cpld_bm = reg_file.readline().rstrip().zfill(node[1]/4)
+ bitmap.append(cpld_bm)
+ reg_file.close()
+
+ rev = "".join(bitmap[::-1])
+ return int(rev,16)
+
+ data = {'valid':0, 'last':0, 'present':0}
+ def get_transceiver_change_event(self, timeout=2000):
+ now = time.time()
+ port_dict = {}
+ port = 0
+
+ if timeout < 1000:
+ timeout = 1000
+ timeout = (timeout) / float(1000) # Convert to secs
+
+ if now < (self.data['last'] + timeout) and self.data['valid']:
+ return True, {}
+
+ reg_value = self._get_present_bitmap
+ changed_ports = self.data['present'] ^ reg_value
+ if changed_ports:
+ for port in range (self.port_start, self.port_end+1):
+ # Mask off the bit corresponding to our port
+ mask = (1 << (port - 1))
+ if changed_ports & mask:
+ if (reg_value & mask) == 0:
+ port_dict[port] = SFP_STATUS_REMOVED
+ else:
+ port_dict[port] = SFP_STATUS_INSERTED
+
+ # Update cache
+ self.data['present'] = reg_value
+ self.data['last'] = now
+ self.data['valid'] = 1
+ pprint.pprint(port_dict)
+ return True, port_dict
+ else:
+ return True, {}
+ return False, {}
+
+
diff --git a/platform/broadcom/one-image.mk b/platform/broadcom/one-image.mk
index edc870fa8f..0edb6ebc4f 100644
--- a/platform/broadcom/one-image.mk
+++ b/platform/broadcom/one-image.mk
@@ -31,6 +31,7 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \
$(ACCTON_AS9716_32D_PLATFORM_MODULE) \
$(ACCTON_AS5835_54T_PLATFORM_MODULE) \
$(ACCTON_AS7312_54XS_PLATFORM_MODULE) \
+ $(ACCTON_AS7315_27XB_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 6745bb5923..37ef2669fb 100755
--- a/platform/broadcom/platform-modules-accton.mk
+++ b/platform/broadcom/platform-modules-accton.mk
@@ -16,6 +16,7 @@ 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
+ACCTON_AS7315_27XB_PLATFORM_MODULE_VERSION = 1.1
export ACCTON_AS7712_32X_PLATFORM_MODULE_VERSION
export ACCTON_AS5712_54X_PLATFORM_MODULE_VERSION
@@ -33,6 +34,7 @@ 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
+export ACCTON_AS7315_27XB_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
@@ -100,4 +102,8 @@ ACCTON_AS5835_54T_PLATFORM_MODULE = sonic-platform-accton-as5835-54t_$(ACCTON_AS
$(ACCTON_AS5835_54T_PLATFORM_MODULE)_PLATFORM = x86_64-accton_as5835_54t-r0
$(eval $(call add_extra_package,$(ACCTON_AS7712_32X_PLATFORM_MODULE),$(ACCTON_AS5835_54T_PLATFORM_MODULE)))
+ACCTON_AS7315_27XB_PLATFORM_MODULE = sonic-platform-accton-as7315-27xb_$(ACCTON_AS7315_27XB_PLATFORM_MODULE_VERSION)_amd64.deb
+$(ACCTON_AS7315_27XB_PLATFORM_MODULE)_PLATFORM = x86_64-accton_as7315_27xb-r0
+$(eval $(call add_extra_package,$(ACCTON_AS7712_32X_PLATFORM_MODULE),$(ACCTON_AS7315_27XB_PLATFORM_MODULE)))
+
SONIC_STRETCH_DEBS += $(ACCTON_AS7712_32X_PLATFORM_MODULE)
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/classes/__init__.py b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/classes/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/classes/fanutil.py b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/classes/fanutil.py
new file mode 100644
index 0000000000..0e7b06bbd2
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/classes/fanutil.py
@@ -0,0 +1,214 @@
+#!/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
+# 5/02/2019: Roy Lee modify for as7816_64x
+# ------------------------------------------------------------------
+
+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_TOTAL_NUM = 5
+ FAN_NUM_1_IDX = 1
+
+ FAN_NODE_NUM = 2
+ FAN_FAULT_IDX = 1
+ #FAN_SPEED_IDX = 2
+ FAN_DIR_IDX = 2
+ #FAN_NODE_DUTY_IDX_OF_MAP = 4
+ #FANR_NODE_FAULT_IDX_OF_MAP = 5
+
+ BASE_VAL_PATH = '/sys/bus/i2c/devices/50-0066/{0}'
+ FAN_DUTY_PATH = '/sys/bus/i2c/devices/50-0066/fan{0}_pwm'
+
+ #logfile = ''
+ #loglevel = self.logger.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) """
+ dev_paths = {}
+
+ node_postfix = ["fault", "direction"]
+ def _get_fan_to_device_node(self, fan_num, node_num):
+ return "fan{0}_{1}".format(fan_num, self.node_postfix[node_num-1])
+
+ def _get_fan_node_val(self, fan_num, node_num):
+ if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_TOTAL_NUM:
+ self.logger.debug('GET. Parameter error. fan_num:%d', fan_num)
+ return None
+
+ if node_num < self.FAN_FAULT_IDX or node_num > self.FAN_NODE_NUM:
+ 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_TOTAL_NUM:
+ self.logger.debug('GET. Parameter error. fan_num:%d', fan_num)
+ return None
+
+ if node_num < self.FAN_FAULT_IDX or node_num > self.FAN_NODE_NUM:
+ 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
+
+ logger = logging.getLogger(__name__)
+ def __init__(self, log_level=logging.DEBUG):
+ 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_TOTAL_NUM+1):
+ for node_num in range(1, self.FAN_NODE_NUM+1):
+ node = self._get_fan_to_device_node(fan_num, node_num)
+ self.dev_paths[(fan_num, node_num)] = fan_path.format(node)
+
+ def get_num_fans(self):
+ return self.FAN_TOTAL_NUM
+
+ def get_idx_fan_start(self):
+ return self.FAN_NUM_1_IDX
+
+ def get_num_nodes(self):
+ return self.FAN_NODE_NUM
+
+ def get_idx_node_start(self):
+ return self.FAN_FAULT_IDX
+
+ def get_size_node_map(self):
+ return len(self.dev_paths)
+
+ def get_size_path_map(self):
+ return len(self.dev_paths)
+
+ def get_fan_to_device_path(self, fan_num, node_num):
+ return self.dev_paths[(fan_num, node_num)]
+
+ def get_fan_fault(self, fan_num):
+ return self._get_fan_node_val(fan_num, self.FAN_FAULT_IDX)
+
+ #def get_fan_speed(self, fan_num):
+ # return self._get_fan_node_val(fan_num, self.FAN_SPEED_IDX)
+
+ def get_fan_dir(self, fan_num):
+ return self._get_fan_node_val(fan_num, self.FAN_DIR_IDX)
+
+ def get_fan_duty_cycle(self):
+ try:
+ val_file = open(self.FAN_DUTY_PATH.format(1))
+ 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)
+
+ def set_fan_duty_cycle(self, val):
+ for fan_num in range(1, self.FAN_TOTAL_NUM+1):
+ try:
+ fan_file = open(self.FAN_DUTY_PATH.format(fan_num), 'r+')
+ except IOError as e:
+ print "Error: unable to open file: %s" % str(e)
+ return False
+ fan_file.write(str(val))
+ fan_file.close()
+ return True
+
+ def get_fan_status(self, fan_num):
+ if fan_num < self.FAN_NUM_1_IDX or fan_num > self.FAN_TOTAL_NUM:
+ 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
+
+ #if self.get_fanr_fault(fan_num) is not None and self.get_fanr_fault(fan_num) > 0:
+ # self.logger.debug('GET. FANR fault. fan_num, %d', fan_num)
+ # return False
+
+ return True
+
+#def main():
+# fan = FanUtil()
+#
+# print 'get_size_node_map : %d' % fan.get_size_node_map()
+# print 'get_size_path_map : %d' % fan.get_size_path_map()
+# for x in range(fan.get_idx_fan_start(), fan.get_num_fans()+1):
+# for y in range(fan.get_idx_node_start(), fan.get_num_nodes()+1):
+# print fan.get_fan_to_device_path(x, y)
+#
+#if __name__ == '__main__':
+# main()
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/classes/thermalutil.py b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/classes/thermalutil.py
new file mode 100644
index 0000000000..47dba67f2c
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/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
+# 5/02/2019: Roy Lee modify for as7816_64x
+# ------------------------------------------------------------------
+
+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
+ 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 = [
+ ['51', '49'],
+ ['52', '4a'],
+ ['53', '4c'],
+ ]
+ logger = logging.getLogger(__name__)
+ def __init__(self, log_level=logging.DEBUG):
+ ch = logging.StreamHandler()
+ ch.setLevel(log_level)
+ self.logger.addHandler(ch)
+
+ thermal_path = self.BASE_VAL_PATH
+ for x in range(self.THERMAL_NUM_ON_MAIN_BROAD):
+ self._thermal_to_device_path_mapping[x+1] = 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_2_val(self):
+ return self._get_thermal_node_val(self.THERMAL_NUM_2_IDX)
+
+ def get_thermal_temp(self):
+ sum = 0
+ o = []
+ for x in range(self.THERMAL_NUM_ON_MAIN_BROAD):
+ sum += self._get_thermal_node_val(x+1)
+ avg = sum/self.THERMAL_NUM_ON_MAIN_BROAD
+ avg = (avg/1000)*1000 #round down for hysteresis.
+ return avg
+
+#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/as7315-27xb/modules/Makefile b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/Makefile
new file mode 100644
index 0000000000..a1bf833537
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/Makefile
@@ -0,0 +1,2 @@
+obj-m:= accton_as7315_27xb_fan.o x86-64-accton-as7315-27xb-cpld.o x86-64-accton-as7315-27xb-psu.o \
+ at24_as7315_27xb.o x86-64-accton-as7315-27xb-led.o ym2651y.o
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/accton_as7315_27xb_fan.c b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/accton_as7315_27xb_fan.c
new file mode 100644
index 0000000000..adf8fc69ea
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/accton_as7315_27xb_fan.c
@@ -0,0 +1,673 @@
+/*
+ * A hwmon driver for the Accton as5710 54x fan contrl
+ *
+ * Copyright (C) 2013 Accton Technology Corporation.
+ * Brandon Chuang
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#define DRV_NAME "as5812_54x_fan"
+
+#define FAN_MAX_NUMBER 5
+#define FAN_SPEED_TACH_TO_RPM_STEP 175
+#define FAN_SPEED_PWM_STEPS 31
+#define FAN_DUTY_CYCLE_MIN 0 /* 10% ??*/
+#define FAN_DUTY_CYCLE_MAX 100 /* 100% */
+
+
+#define I2C_RW_RETRY_COUNT 10
+#define I2C_RW_RETRY_INTERVAL 60 /* ms */
+#define ATTR_ALLOC_SIZE 1 /*For last attribute which is NUll.*/
+#define NAME_SIZE 24
+
+typedef ssize_t (*show_func)( struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+typedef ssize_t (*store_func)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+
+struct fan_sensor {
+ struct fan_sensor *next;
+ char name[NAME_SIZE+1]; /* sysfs sensor name */
+ struct device_attribute attribute;
+ bool update; /* runtime sensor update needed */
+ int data; /* Sensor data. Negative if there was a read error */
+
+ u8 reg; /* register */
+ u8 mask; /* bit mask */
+ bool invert; /* inverted value*/
+
+};
+
+#define to_fan_sensor(_attr) \
+ container_of(_attr, struct fan_sensor, attribute)
+
+
+struct model_attrs {
+ struct attrs **cmn;
+ struct attrs **indiv;
+};
+
+
+struct fan_data_t {
+ struct device *dev;
+ struct device *hwmon_dev;
+
+ int num_attributes;
+ struct attribute_group group;
+ struct fan_sensor *sensors;
+ int attr_index;
+ struct model_attrs *attrs;
+
+ struct mutex update_lock;
+ char valid; /* != 0 if registers are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u8 fan_num;
+
+ u8 status[FAN_MAX_NUMBER]; /* inner first fan status */
+ u32 speed[FAN_MAX_NUMBER]; /* inner first fan speed */
+ u8 direction[FAN_MAX_NUMBER]; /* reconrd the direction of inner first and second fans */
+ u32 duty_cycle[FAN_MAX_NUMBER]; /* control the speed of inner first and second fans */
+ u8 r_status[FAN_MAX_NUMBER]; /* inner second fan status */
+ u32 r_speed[FAN_MAX_NUMBER]; /* inner second fan speed */
+};
+
+static ssize_t show_bit(struct device *dev,
+ struct device_attribute *devattr, char *buf);
+static ssize_t show_byte(struct device *dev,
+ struct device_attribute *devattr, char *buf);
+static ssize_t set_1bit(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count);
+static ssize_t set_pwm(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count);
+static ssize_t show_pwm(struct device *dev,
+ struct device_attribute *devattr, char *buf);
+static ssize_t show_rpm(struct device *dev,
+ struct device_attribute *devattr, char *buf);
+
+struct base_attrs {
+ const char *name;
+ umode_t mode;
+ show_func get;
+ store_func set;
+};
+
+struct attrs {
+ int reg;
+ int mask;
+ bool invert;
+ struct base_attrs *base;
+};
+
+enum common_attrs {
+ BRD_VERSION,
+ PLD_VERSION,
+ PLD_SVERSION,
+ NUM_COMMON_ATTR
+};
+
+struct base_attrs common_base_attrs[NUM_COMMON_ATTR] =
+{
+ [BRD_VERSION] = {"borad_ver", S_IRUGO, show_byte, NULL},
+ [PLD_VERSION] = {"cpld_ver", S_IRUGO, show_byte, NULL},
+ [PLD_SVERSION] = {"cpld_subver", S_IRUGO, show_byte, NULL},
+};
+
+struct attrs common_attrs[] = {
+ [BRD_VERSION] = {0x00, -1, false, &common_base_attrs[BRD_VERSION]},
+ [PLD_VERSION] = {0x01, -1, false, &common_base_attrs[PLD_VERSION]},
+ [PLD_SVERSION] = {0x02, -1, false, &common_base_attrs[PLD_SVERSION]},
+};
+
+struct attrs *as7315_cmn_list[] = {
+ &common_attrs[BRD_VERSION],
+ &common_attrs[PLD_VERSION],
+ &common_attrs[PLD_SVERSION],
+ NULL
+};
+
+enum fan_attrs {
+ _ENABLE,
+ _PRESENT,
+ _FAULT,
+ _SPEED_RPM,
+ _PWM,
+ _DIRECTION,
+ NUM_FAN_ATTRS
+};
+
+struct base_attrs tray_base_attrs[NUM_FAN_ATTRS] =
+{
+ {"enable", S_IRUGO|S_IWUSR, show_bit, set_1bit},
+ {"present", S_IRUGO, show_bit, NULL},
+ {"fault", S_IRUGO, show_bit, NULL},
+ {"input", S_IRUGO, show_rpm, NULL},
+ {"pwm", S_IRUGO|S_IWUSR, show_pwm, set_pwm},
+ {"dir", S_IRUGO, show_bit, NULL},
+};
+
+struct attrs as7315_module[NUM_FAN_ATTRS] = {
+ {0x10, -1, false, &tray_base_attrs[_ENABLE]},
+ {0x22, 0, true, &tray_base_attrs[_PRESENT]},
+ {0x22, 1, false, &tray_base_attrs[_FAULT]},
+ {0x20, 0, false, &tray_base_attrs[_SPEED_RPM]},
+ {0x21, 0, false, &tray_base_attrs[_PWM]},
+ {-1, -1, false, &tray_base_attrs[_DIRECTION]},
+};
+
+struct attrs *as7315_mod_list[] = {
+ &as7315_module[_ENABLE],
+ &as7315_module[_PRESENT],
+ &as7315_module[_FAULT],
+ &as7315_module[_SPEED_RPM],
+ &as7315_module[_PWM],
+ NULL
+};
+
+struct model_attrs models_attr = {
+ .cmn = as7315_cmn_list,
+ .indiv = as7315_mod_list,
+};
+
+
+static const struct i2c_device_id as7315_fan_id[] = {
+ { "as7315_fan", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, as7315_fan_id);
+
+
+static int cpld_write_internal(
+ struct i2c_client *client, u8 reg, u8 value)
+{
+ int status = 0, retry = I2C_RW_RETRY_COUNT;
+
+ while (retry) {
+ status = i2c_smbus_write_byte_data(client, reg, value);
+ if (unlikely(status < 0)) {
+ msleep(I2C_RW_RETRY_INTERVAL);
+ retry--;
+ continue;
+ }
+ break;
+ }
+ return status;
+}
+
+static int cpld_read_internal(struct i2c_client *client, u8 reg)
+{
+ int status = 0, retry = I2C_RW_RETRY_COUNT;
+
+ while (retry) {
+ status = i2c_smbus_read_byte_data(client, reg);
+ if (unlikely(status < 0)) {
+ msleep(I2C_RW_RETRY_INTERVAL);
+ retry--;
+ continue;
+ }
+ break;
+ }
+ return status;
+}
+
+static ssize_t show_bit(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int value;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct fan_data_t *data = i2c_get_clientdata(client);
+ struct fan_sensor *sensor = to_fan_sensor(devattr);
+
+ mutex_lock(&data->update_lock);
+ value = cpld_read_internal(client, sensor->reg);
+ if (unlikely(value < 0)) {
+ mutex_unlock(&data->update_lock);
+ return value;
+ }
+ value = value & sensor->mask;
+ if (sensor->invert)
+ value = !value;
+ mutex_unlock(&data->update_lock);
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", !!value);
+}
+
+static ssize_t _read_1byte(struct device *dev,
+ struct device_attribute *devattr, u8 *data)
+{
+ int rv;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct fan_data_t *fdata = i2c_get_clientdata(client);
+ struct fan_sensor *sensor = to_fan_sensor(devattr);
+
+ mutex_lock(&fdata->update_lock);
+ rv = cpld_read_internal(client, sensor->reg);
+ if (unlikely(rv < 0)) {
+ mutex_unlock(&fdata->update_lock);
+ return rv;
+ }
+ mutex_unlock(&fdata->update_lock);
+ *data = rv;
+ return 0;
+}
+
+static ssize_t show_byte(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ u8 data;
+ int rv;
+
+ rv =_read_1byte(dev, devattr, &data);
+ if (unlikely(rv < 0)) {
+ return rv;
+ }
+ return snprintf(buf, PAGE_SIZE, "0x%x\n", data);
+}
+
+
+static u32 reg_val_to_duty_cycle(u8 reg_val)
+{
+ return ((u32)(reg_val+1) * 156 + 88) / 100;
+}
+static u8 duty_cycle_to_reg_val(u8 duty_cycle)
+{
+ return ((u32)duty_cycle * 100 / 155) - 1;
+}
+
+static ssize_t show_pwm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ u8 data;
+ int rv;
+
+ rv =_read_1byte(dev, devattr, &data);
+ if (unlikely(rv < 0)) {
+ return rv;
+ }
+ data = reg_val_to_duty_cycle(data);
+ data = (data > FAN_DUTY_CYCLE_MAX)? FAN_DUTY_CYCLE_MAX: data;
+ return snprintf(buf, PAGE_SIZE, "%d\n", data);
+}
+
+static ssize_t show_rpm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ u8 data;
+ int rv;
+
+ rv =_read_1byte(dev, devattr, &data);
+ if (unlikely(rv < 0)) {
+ return rv;
+ }
+ rv = data * FAN_SPEED_TACH_TO_RPM_STEP;
+ return snprintf(buf, PAGE_SIZE, "%d\n", rv);
+}
+static ssize_t set_1bit(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ long is_reset;
+ int value, status;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct fan_data_t *data = i2c_get_clientdata(client);
+ struct fan_sensor *sensor = to_fan_sensor(devattr);
+ u8 cpld_bit, reg;
+
+ status = kstrtol(buf, 10, &is_reset);
+ if (status) {
+ return status;
+ }
+ reg = sensor->reg;
+ cpld_bit = sensor->mask;
+ mutex_lock(&data->update_lock);
+ value = cpld_read_internal(client, reg);
+ if (unlikely(status < 0)) {
+ goto exit;
+ }
+
+ if (sensor->invert)
+ is_reset = !is_reset;
+
+ if (is_reset) {
+ value |= cpld_bit;
+ }
+ else {
+ value &= ~cpld_bit;
+ }
+
+ status = cpld_write_internal(client, reg, value);
+ if (unlikely(status < 0)) {
+ goto exit;
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+
+exit:
+ mutex_unlock(&data->update_lock);
+ return status;
+}
+
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ long value;
+ int status;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct fan_data_t *data = i2c_get_clientdata(client);
+ struct fan_sensor *sensor = to_fan_sensor(devattr);
+ u8 reg;
+
+ status = kstrtol(buf, 10, &value);
+ if (status) {
+ return status;
+ }
+
+ value = (value > FAN_DUTY_CYCLE_MAX )? FAN_DUTY_CYCLE_MAX: value;
+ value = duty_cycle_to_reg_val(value);
+ reg = sensor->reg;
+
+ mutex_lock(&data->update_lock);
+ status = cpld_write_internal(client, reg, value);
+ if (unlikely(status < 0)) {
+ mutex_unlock(&data->update_lock);
+ return status;
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+
+static int _add_attribute(struct fan_data_t *data, struct attribute *attr)
+{
+ int new_max_attrs = ++data->num_attributes + ATTR_ALLOC_SIZE;
+ void *new_attrs = krealloc(data->group.attrs,
+ new_max_attrs * sizeof(void *),
+ GFP_KERNEL);
+
+ if (!new_attrs)
+ return -ENOMEM;
+
+ data->group.attrs = new_attrs;
+ data->group.attrs[data->num_attributes-1] = attr;
+ data->group.attrs[data->num_attributes] = NULL;
+
+ return 0;
+}
+
+static void cpld_dev_attr_init(struct device_attribute *dev_attr,
+ const char *name, umode_t mode,
+ show_func show, store_func store)
+{
+ sysfs_attr_init(&dev_attr->attr);
+ dev_attr->attr.name = name;
+ dev_attr->attr.mode = mode;
+ dev_attr->show = show;
+ dev_attr->store = store;
+}
+
+static struct fan_sensor * _add_sensor(struct fan_data_t *data,
+ const char *name,
+ u8 reg, u8 mask, bool invert,
+ bool update, umode_t mode,
+ show_func get, store_func set)
+{
+ struct fan_sensor *sensor;
+ struct device_attribute *a;
+
+ sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return NULL;
+ a = &sensor->attribute;
+
+ snprintf(sensor->name, sizeof(sensor->name), name);
+ sensor->reg = reg;
+ sensor->mask = mask;
+ sensor->update = update;
+ sensor->invert = invert;
+ cpld_dev_attr_init(a, sensor->name,
+ mode,
+ get, set);
+
+ if (_add_attribute(data, &a->attr))
+ return NULL;
+
+ sensor->next = data->sensors;
+ data->sensors = sensor;
+
+ return sensor;
+}
+
+static int _add_attributes_cmn(struct fan_data_t *data, struct attrs **cmn)
+{
+ u8 reg, i ;
+ bool invert;
+ struct attrs *a;
+ struct base_attrs *b;
+
+ if (NULL == cmn)
+ return -1;
+
+ for (i = 0; cmn[i]; i++)
+ {
+ a = cmn[i];
+ reg = a->reg;
+ invert = a->invert;
+
+
+
+ b = a->base;
+ if (NULL == b)
+ break;
+
+
+ if (_add_sensor(data, b->name,
+ reg, 0xff, invert,
+ true, b->mode,
+ b->get, b->set) == NULL)
+ {
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+static int _add_attributes_indiv(struct fan_data_t *data, struct attrs **pa)
+{
+ char name[NAME_SIZE+1];
+ int i, j, mask;
+ u8 reg, invert, reg_start, jump;
+ struct attrs *a;
+ struct base_attrs *b;
+
+ if (NULL == pa)
+ return -EFAULT;
+
+ jump = 0x10;
+ for (i = 0; pa[i]; i++) {
+ a = pa[i];
+ reg_start = a->reg;
+
+ if (reg < 0)
+ break;
+
+ b = a->base;
+ if (b == NULL)
+ break;
+ invert = a->invert;
+ for (j = 0; j < data->fan_num; j++)
+ {
+
+ snprintf(name, NAME_SIZE, "fan%d_%s", j+1, b->name);
+ /*If mask < 0, mask is as index*/
+ if (a->mask < 0) {
+ mask = 1 << (j%8);
+ reg = reg_start;
+ } else { /*If mask >= 0, means to get full byte and reg need to shift*/
+ mask = 1 << (a->mask);
+ reg = reg_start + ((j%8)*jump);
+ }
+ if (_add_sensor(data, name, reg, mask, invert,
+ true, b->mode, b->get, b->set) == NULL)
+ {
+ return -ENOMEM;
+ }
+ }
+ }
+
+
+ return 0;
+}
+
+static int _add_attributes(struct i2c_client *client,
+ struct fan_data_t *data)
+{
+ struct model_attrs *m = data->attrs;
+
+ if (m == NULL)
+ return -EINVAL;
+
+ /* Common attributes.*/
+ _add_attributes_cmn(data, m->cmn);
+
+ /* Port-wise attributes.*/
+ _add_attributes_indiv(data, m->indiv);
+
+ return 0;
+}
+
+static int fan_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ int status;
+ struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
+ struct fan_data_t *data = NULL;
+ struct device *dev = &client->dev;
+
+
+ if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ return -ENOMEM;
+ }
+
+ data->attrs = &models_attr;
+ data->fan_num = FAN_MAX_NUMBER;
+ mutex_init(&data->update_lock);
+ data->dev = dev;
+ dev_info(dev, "chip found\n");
+
+
+ status = _add_attributes(client, data);
+ if (status) {
+ return status;
+ }
+
+ if (!data->num_attributes) {
+ dev_err(&client->dev, "No attributes found\n");
+ return -ENODEV;
+ }
+
+ /* Register sysfs hooks */
+ status = sysfs_create_group(&client->dev.kobj, &data->group);
+ if (status) {
+ goto out_kfree;
+ }
+
+ data->hwmon_dev = hwmon_device_register_with_info(&client->dev,
+ client->name, NULL, NULL, NULL);
+ if (IS_ERR(data->hwmon_dev)) {
+ status = PTR_ERR(data->hwmon_dev);
+ goto exit_rm_sys;
+ }
+
+ i2c_set_clientdata(client, data);
+ dev_info(dev, "%s: cpld '%s'\n",
+ dev_name(data->dev), client->name);
+
+ return 0;
+
+exit_rm_sys:
+ sysfs_remove_group(&client->dev.kobj, &data->group);
+out_kfree:
+ kfree(data->group.attrs);
+ return status;
+}
+
+
+static int fan_remove(struct i2c_client *client)
+{
+ struct fan_data_t *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &data->group);
+ kfree(data->group.attrs);
+ return 0;
+}
+
+
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static struct i2c_driver as7315_i2c_fan_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = fan_probe,
+ .remove = fan_remove,
+ .id_table = as7315_fan_id,
+ .address_list = normal_i2c,
+};
+
+static int __init accton_as7315_27xb_fan_init(void)
+{
+ return i2c_add_driver(&as7315_i2c_fan_driver);
+}
+
+static void __exit accton_as7315_27xb_fan_exit(void)
+{
+ i2c_del_driver(&as7315_i2c_fan_driver);
+}
+
+MODULE_AUTHOR("Brandon Chuang ");
+MODULE_DESCRIPTION("accton_as7315_27xb_fan driver");
+MODULE_LICENSE("GPL");
+
+module_init(accton_as7315_27xb_fan_init);
+module_exit(accton_as7315_27xb_fan_exit);
+
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/at24_as7315_27xb.c b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/at24_as7315_27xb.c
new file mode 100755
index 0000000000..8d8c7601a7
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/at24_as7315_27xb.c
@@ -0,0 +1,695 @@
+/*
+ * at24_as7315_27xb.c - handle most I2C EEPROMs for ethernet switch, as7315_27xb.
+ *
+ * Copyright (C) 2005-2007 David Brownell
+ * Copyright (C) 2008 Wolfram Sang, Pengutronix
+ * Copyright (C) 2019 Roy Lee, EdgeCore network.
+ * Revised to support specially I2C transactions, pure READ and WRITE.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+/*
+ * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
+ * Differences between different vendor product lines (like Atmel AT24C or
+ * MicroChip 24LC, etc) won't much matter for typical read/write access.
+ * There are also I2C RAM chips, likewise interchangeable. One example
+ * would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes).
+ *
+ * However, misconfiguration can lose data. "Set 16-bit memory address"
+ * to a part with 8-bit addressing will overwrite data. Writing with too
+ * big a page size also loses data. And it's not safe to assume that the
+ * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC
+ * uses 0x51, for just one example.
+ *
+ * Accordingly, explicit board-specific configuration data should be used
+ * in almost all cases. (One partial exception is an SMBus used to access
+ * "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.)
+ *
+ * So this driver uses "new style" I2C driver binding, expecting to be
+ * told what devices exist. That may be in arch/X/mach-Y/board-Z.c or
+ * similar kernel-resident tables; or, configuration data coming from
+ * a bootloader.
+ *
+ * Other than binding model, current differences from "eeprom" driver are
+ * that this one handles write access and isn't restricted to 24c02 devices.
+ * It also handles larger devices (32 kbit and up) with two-byte addresses,
+ * which won't work on pure SMBus systems.
+ */
+
+struct at24_data {
+ struct at24_platform_data chip;
+ int use_smbus;
+ int use_smbus_write;
+
+ ssize_t (*read_func)(struct at24_data *, char *, unsigned int, size_t);
+ ssize_t (*write_func)(struct at24_data *,
+ const char *, unsigned int, size_t);
+
+ /*
+ * Lock protects against activities from other Linux tasks,
+ * but not from changes by other I2C masters.
+ */
+ struct mutex lock;
+
+ u8 *writebuf;
+ unsigned write_max;
+ unsigned num_addresses;
+
+ struct nvmem_config nvmem_config;
+ struct nvmem_device *nvmem;
+
+ /*
+ * Some chips tie up multiple I2C addresses; dummy devices reserve
+ * them for us, and we'll use them with SMBus calls.
+ */
+ struct i2c_client *client[];
+};
+
+/*
+ * This parameter is to help this driver avoid blocking other drivers out
+ * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C
+ * clock, one 256 byte read takes about 1/43 second which is excessive;
+ * but the 1/170 second it takes at 400 kHz may be quite reasonable; and
+ * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible.
+ *
+ * This value is forced to be a power of two so that writes align on pages.
+ */
+static unsigned io_limit = 128;
+module_param(io_limit, uint, 0);
+MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)");
+
+/*
+ * Specs often allow 5 msec for a page write, sometimes 20 msec;
+ * it's important to recover from write timeouts.
+ */
+static unsigned write_timeout = 25;
+module_param(write_timeout, uint, 0);
+MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)");
+
+#define AT24_SIZE_BYTELEN 5
+#define AT24_SIZE_FLAGS 8
+
+#define AT24_BITMASK(x) (BIT(x) - 1)
+
+/* create non-zero magic value for given eeprom parameters */
+#define AT24_DEVICE_MAGIC(_len, _flags) \
+ ((1 << AT24_SIZE_FLAGS | (_flags)) \
+ << AT24_SIZE_BYTELEN | ilog2(_len))
+
+/*
+ * Both reads and writes fail if the previous write didn't complete yet. This
+ * macro loops a few times waiting at least long enough for one entire page
+ * write to work while making sure that at least one iteration is run before
+ * checking the break condition.
+ *
+ * It takes two parameters: a variable in which the future timeout in jiffies
+ * will be stored and a temporary variable holding the time of the last
+ * iteration of processing the request. Both should be unsigned integers
+ * holding at least 32 bits.
+ */
+#define loop_until_timeout(tout, op_time) \
+ for (tout = jiffies + msecs_to_jiffies(write_timeout), op_time = 0; \
+ op_time ? time_before(op_time, tout) : true; \
+ usleep_range(1000, 1500), op_time = jiffies)
+
+static const struct i2c_device_id at24_ids[] = {
+ { "24cxb04", AT24_DEVICE_MAGIC(4096 / 8, AT24_FLAG_ADDR16) },
+ { "24cxb08", AT24_DEVICE_MAGIC(8192 / 8, AT24_FLAG_ADDR16) },
+ { "24cxb16", AT24_DEVICE_MAGIC(16384 / 8, AT24_FLAG_ADDR16) },
+ { "24cxb32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
+ { "24cxb64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
+ { "at24", 0 },
+ { /* END OF LIST */ }
+};
+MODULE_DEVICE_TABLE(i2c, at24_ids);
+
+static const struct acpi_device_id at24_acpi_ids[] = {
+ { "INT3499", AT24_DEVICE_MAGIC(8192 / 8, 0) },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, at24_acpi_ids);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * This routine supports chips which consume multiple I2C addresses. It
+ * computes the addressing information to be used for a given r/w request.
+ * Assumes that sanity checks for offset happened at sysfs-layer.
+ *
+ * Slave address and byte offset derive from the offset. Always
+ * set the byte address; on a multi-master board, another master
+ * may have changed the chip's "current" address pointer.
+ *
+ * REVISIT some multi-address chips don't rollover page reads to
+ * the next slave address, so we may need to truncate the count.
+ * Those chips might need another quirk flag.
+ *
+ * If the real hardware used four adjacent 24c02 chips and that
+ * were misconfigured as one 24c08, that would be a similar effect:
+ * one "eeprom" file not four, but larger reads would fail when
+ * they crossed certain pages.
+ */
+static struct i2c_client *at24_translate_offset(struct at24_data *at24,
+ unsigned int *offset)
+{
+ unsigned i;
+
+ /*Always AT24_FLAG_ADDR16*/
+ i = *offset >> 16;
+ *offset &= 0xffff;
+
+ return at24->client[i];
+}
+
+static ssize_t at24_eeprom_read_smbus(struct at24_data *at24, char *buf,
+ unsigned int offset, size_t count)
+{
+ unsigned long timeout, read_time;
+ struct i2c_client *client;
+ int status;
+ client = at24_translate_offset(at24, &offset);
+
+ if (count > io_limit)
+ count = io_limit;
+
+ loop_until_timeout(timeout, read_time) {
+/*
+ status = i2c_smbus_read_i2c_block_data_or_emulated(client,
+ offset,
+ count, buf);
+
+ dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
+ count, offset, status, jiffies);
+*/
+ int i=0;
+
+ /*Write 2-byte offset*/
+ unsigned char bb = offset&0xff;
+ status = i2c_smbus_write_i2c_block_data(client, offset>>8, 1, &bb);
+ if (status < 0) {
+ return status;
+ }
+
+ while (i < count) {
+ status = i2c_smbus_read_byte(client);
+ if (status < 0) {
+ return status;
+ }
+ buf[i] = status;
+ i++;
+ }
+
+
+ if (i == count)
+ return count;
+ }
+
+ return -ETIMEDOUT;
+}
+
+/*
+ * Note that if the hardware write-protect pin is pulled high, the whole
+ * chip is normally write protected. But there are plenty of product
+ * variants here, including OTP fuses and partial chip protect.
+ *
+ * We only use page mode writes; the alternative is sloooow. These routines
+ * write at most one page.
+ */
+
+static size_t at24_adjust_write_count(struct at24_data *at24,
+ unsigned int offset, size_t count)
+{
+ unsigned next_page;
+
+ /* write_max is at most a page */
+ if (count > at24->write_max)
+ count = at24->write_max;
+
+ /* Never roll over backwards, to the start of this page */
+ next_page = roundup(offset + 1, at24->chip.page_size);
+ if (offset + count > next_page)
+ count = next_page - offset;
+
+ return count;
+}
+
+static ssize_t at24_eeprom_write_smbus_block(struct at24_data *at24,
+ const char *buf,
+ unsigned int offset, size_t count)
+{
+ unsigned long timeout, write_time;
+ struct i2c_client *client;
+ ssize_t status = 0;
+
+ client = at24_translate_offset(at24, &offset);
+ count = at24_adjust_write_count(at24, offset, count);
+
+ loop_until_timeout(timeout, write_time) {
+ status = i2c_smbus_write_i2c_block_data(client,
+ offset, count, buf);
+ if (status == 0)
+ status = count;
+
+ dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
+ count, offset, status, jiffies);
+
+ if (status == count)
+ return count;
+ }
+
+ return -ETIMEDOUT;
+}
+
+static ssize_t at24_eeprom_write_smbus_byte(struct at24_data *at24,
+ const char *buf,
+ unsigned int offset, size_t count)
+{
+ unsigned long timeout, write_time;
+ struct i2c_client *client;
+ ssize_t status = 0;
+
+ client = at24_translate_offset(at24, &offset);
+
+ loop_until_timeout(timeout, write_time) {
+ status = i2c_smbus_write_byte_data(client, offset, buf[0]);
+ if (status == 0)
+ status = count;
+
+ dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
+ count, offset, status, jiffies);
+
+ if (status == count)
+ return count;
+ }
+
+ return -ETIMEDOUT;
+}
+
+static ssize_t at24_eeprom_write_i2c(struct at24_data *at24, const char *buf,
+ unsigned int offset, size_t count)
+{
+ unsigned long timeout, write_time;
+ struct i2c_client *client;
+ struct i2c_msg msg;
+ ssize_t status = 0;
+ int i = 0;
+
+ client = at24_translate_offset(at24, &offset);
+ count = at24_adjust_write_count(at24, offset, count);
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+
+ /* msg.buf is u8 and casts will mask the values */
+ msg.buf = at24->writebuf;
+ if (at24->chip.flags & AT24_FLAG_ADDR16)
+ msg.buf[i++] = offset >> 8;
+
+ msg.buf[i++] = offset;
+ memcpy(&msg.buf[i], buf, count);
+ msg.len = i + count;
+
+ loop_until_timeout(timeout, write_time) {
+ status = i2c_transfer(client->adapter, &msg, 1);
+ if (status == 1)
+ status = count;
+
+ dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
+ count, offset, status, jiffies);
+
+ if (status == count)
+ return count;
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int at24_read(void *priv, unsigned int off, void *val, size_t count)
+{
+ struct at24_data *at24 = priv;
+ char *buf = val;
+
+
+
+ if (unlikely(!count))
+ return count;
+
+ if (off + count > at24->chip.byte_len)
+ return -EINVAL;
+
+ /*
+ * Read data from chip, protecting against concurrent updates
+ * from this host, but not from other I2C masters.
+ */
+ mutex_lock(&at24->lock);
+
+ while (count) {
+ int status;
+
+ status = at24->read_func(at24, buf, off, count);
+ if (status < 0) {
+ mutex_unlock(&at24->lock);
+ return status;
+ }
+ buf += status;
+ off += status;
+ count -= status;
+ }
+
+ mutex_unlock(&at24->lock);
+
+ return 0;
+}
+
+static int at24_write(void *priv, unsigned int off, void *val, size_t count)
+{
+ struct at24_data *at24 = priv;
+ char *buf = val;
+
+ if (unlikely(!count))
+ return -EINVAL;
+
+ if (off + count > at24->chip.byte_len)
+ return -EINVAL;
+
+ /*
+ * Write data to chip, protecting against concurrent updates
+ * from this host, but not from other I2C masters.
+ */
+ mutex_lock(&at24->lock);
+
+ while (count) {
+ int status;
+
+ status = at24->write_func(at24, buf, off, count);
+ if (status < 0) {
+ mutex_unlock(&at24->lock);
+ return status;
+ }
+ buf += status;
+ off += status;
+ count -= status;
+ }
+
+ mutex_unlock(&at24->lock);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static void at24_get_ofdata(struct i2c_client *client,
+ struct at24_platform_data *chip)
+{
+ const __be32 *val;
+ struct device_node *node = client->dev.of_node;
+
+ if (node) {
+ if (of_get_property(node, "read-only", NULL))
+ chip->flags |= AT24_FLAG_READONLY;
+ val = of_get_property(node, "pagesize", NULL);
+ if (val)
+ chip->page_size = be32_to_cpup(val);
+ }
+}
+#else
+static void at24_get_ofdata(struct i2c_client *client,
+ struct at24_platform_data *chip)
+{ }
+#endif /* CONFIG_OF */
+
+static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct at24_platform_data chip;
+ kernel_ulong_t magic = 0;
+ bool writable;
+ int use_smbus = 0;
+ int use_smbus_write = 0;
+ struct at24_data *at24;
+ int err;
+ unsigned i, num_addresses;
+
+ if (client->dev.platform_data) {
+ chip = *(struct at24_platform_data *)client->dev.platform_data;
+ } else {
+ if (id) {
+ magic = id->driver_data;
+ } else {
+ const struct acpi_device_id *aid;
+
+ aid = acpi_match_device(at24_acpi_ids, &client->dev);
+ if (aid)
+ magic = aid->driver_data;
+ }
+ if (!magic)
+ return -ENODEV;
+
+ chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
+ magic >>= AT24_SIZE_BYTELEN;
+ chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
+ /*
+ * This is slow, but we can't know all eeproms, so we better
+ * play safe. Specifying custom eeprom-types via platform_data
+ * is recommended anyhow.
+ */
+ chip.page_size = 1;
+
+ /* update chipdata if OF is present */
+ at24_get_ofdata(client, &chip);
+
+ chip.setup = NULL;
+ chip.context = NULL;
+ }
+
+ if (!is_power_of_2(chip.byte_len))
+ dev_warn(&client->dev,
+ "byte_len looks suspicious (no power of 2)!\n");
+ if (!chip.page_size) {
+ dev_err(&client->dev, "page_size must not be 0!\n");
+ return -EINVAL;
+ }
+ if (!is_power_of_2(chip.page_size))
+ dev_warn(&client->dev,
+ "page_size looks suspicious (no power of 2)!\n");
+
+ /*
+ * REVISIT: the size of the EUI-48 byte array is 6 in at24mac402, while
+ * the call to ilog2() in AT24_DEVICE_MAGIC() rounds it down to 4.
+ *
+ * Eventually we'll get rid of the magic values altoghether in favor of
+ * real structs, but for now just manually set the right size.
+ */
+ if (chip.flags & AT24_FLAG_MAC && chip.byte_len == 4)
+ chip.byte_len = 6;
+
+ /* Use I2C operations unless we're stuck with SMBus extensions. */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /*if (chip.flags & AT24_FLAG_ADDR16){
+ return -EPFNOSUPPORT;
+ }*/
+ if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
+ } else if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+ use_smbus = I2C_SMBUS_WORD_DATA;
+ } else if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
+ use_smbus = I2C_SMBUS_BYTE_DATA;
+ } else {
+ return -EPFNOSUPPORT;
+ }
+
+ if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
+ use_smbus_write = I2C_SMBUS_I2C_BLOCK_DATA;
+ } else if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
+ use_smbus_write = I2C_SMBUS_BYTE_DATA;
+ chip.page_size = 1;
+ }
+ }
+
+ if (chip.flags & AT24_FLAG_TAKE8ADDR)
+ num_addresses = 8;
+ else
+ num_addresses = DIV_ROUND_UP(chip.byte_len,
+ (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
+
+ at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) +
+ num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
+ if (!at24)
+ return -ENOMEM;
+
+ mutex_init(&at24->lock);
+ at24->use_smbus = use_smbus;
+ at24->use_smbus_write = use_smbus_write;
+ at24->chip = chip;
+ at24->num_addresses = num_addresses;
+
+ if ((chip.flags & AT24_FLAG_SERIAL) && (chip.flags & AT24_FLAG_MAC)) {
+ dev_err(&client->dev,
+ "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
+ return -EINVAL;
+ }
+ at24->read_func = at24_eeprom_read_smbus;
+
+ if (at24->use_smbus) {
+ if (at24->use_smbus_write == I2C_SMBUS_I2C_BLOCK_DATA)
+ at24->write_func = at24_eeprom_write_smbus_block;
+ else
+ at24->write_func = at24_eeprom_write_smbus_byte;
+ } else {
+ at24->write_func = at24_eeprom_write_i2c;
+ }
+
+ writable = !(chip.flags & AT24_FLAG_READONLY);
+ if (writable) {
+ if (!use_smbus || use_smbus_write) {
+
+ unsigned write_max = chip.page_size;
+
+ if (write_max > io_limit)
+ write_max = io_limit;
+ if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
+ write_max = I2C_SMBUS_BLOCK_MAX;
+ at24->write_max = write_max;
+
+ /* buffer (data + address at the beginning) */
+ at24->writebuf = devm_kzalloc(&client->dev,
+ write_max + 2, GFP_KERNEL);
+ if (!at24->writebuf)
+ return -ENOMEM;
+ } else {
+ dev_warn(&client->dev,
+ "cannot write due to controller restrictions.");
+ }
+ }
+
+ at24->client[0] = client;
+
+ /* use dummy devices for multiple-address chips */
+ for (i = 1; i < num_addresses; i++) {
+ at24->client[i] = i2c_new_dummy(client->adapter,
+ client->addr + i);
+ if (!at24->client[i]) {
+ dev_err(&client->dev, "address 0x%02x unavailable\n",
+ client->addr + i);
+ err = -EADDRINUSE;
+ goto err_clients;
+ }
+ }
+
+ i2c_set_clientdata(client, at24);
+ at24->nvmem_config.name = dev_name(&client->dev);
+ at24->nvmem_config.dev = &client->dev;
+ at24->nvmem_config.read_only = !writable;
+ at24->nvmem_config.root_only = true;
+ at24->nvmem_config.owner = THIS_MODULE;
+ at24->nvmem_config.compat = true;
+ at24->nvmem_config.base_dev = &client->dev;
+ at24->nvmem_config.reg_read = at24_read;
+ at24->nvmem_config.reg_write = at24_write;
+ at24->nvmem_config.priv = at24;
+ at24->nvmem_config.stride = 1;
+ at24->nvmem_config.word_size = 1;
+ at24->nvmem_config.size = chip.byte_len;
+
+ at24->nvmem = nvmem_register(&at24->nvmem_config);
+
+ if (IS_ERR(at24->nvmem)) {
+ err = PTR_ERR(at24->nvmem);
+ goto err_clients;
+ }
+
+ dev_info(&client->dev, "%u byte %s EEPROM, %s, %u bytes/write\n",
+ chip.byte_len, client->name,
+ writable ? "writable" : "read-only", at24->write_max);
+ if (use_smbus == I2C_SMBUS_WORD_DATA ||
+ use_smbus == I2C_SMBUS_BYTE_DATA) {
+ dev_notice(&client->dev, "Falling back to %s reads, "
+ "performance will suffer\n", use_smbus ==
+ I2C_SMBUS_WORD_DATA ? "word" : "byte");
+ }
+
+ /* export data to kernel code */
+ if (chip.setup)
+ chip.setup(at24->nvmem, chip.context);
+
+ return 0;
+
+err_clients:
+ for (i = 1; i < num_addresses; i++)
+ if (at24->client[i])
+ i2c_unregister_device(at24->client[i]);
+
+ return err;
+}
+
+static int at24_remove(struct i2c_client *client)
+{
+ struct at24_data *at24;
+ int i;
+
+ at24 = i2c_get_clientdata(client);
+
+ nvmem_unregister(at24->nvmem);
+
+ for (i = 1; i < at24->num_addresses; i++)
+ i2c_unregister_device(at24->client[i]);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct i2c_driver at24_as7315_27xb_driver = {
+ .driver = {
+ .name = "at24_as7315_27xb",
+ .acpi_match_table = ACPI_PTR(at24_acpi_ids),
+ },
+ .probe = at24_probe,
+ .remove = at24_remove,
+ .id_table = at24_ids,
+};
+
+static int __init at24_as7315_27xb_init(void)
+{
+ if (!io_limit) {
+ pr_err("at24_as7315_27xb: io_limit must not be 0!\n");
+ return -EINVAL;
+ }
+
+ io_limit = rounddown_pow_of_two(io_limit);
+ return i2c_add_driver(&at24_as7315_27xb_driver);
+}
+module_init(at24_as7315_27xb_init);
+
+static void __exit at24_as7315_27xb_exit(void)
+{
+ i2c_del_driver(&at24_as7315_27xb_driver);
+}
+module_exit(at24_as7315_27xb_exit);
+
+MODULE_DESCRIPTION("Driver for I2C EEPROMs on accton switch, as7315_27xb");
+MODULE_AUTHOR("Roy Lee");
+MODULE_LICENSE("GPL");
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/x86-64-accton-as7315-27xb-cpld.c b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/x86-64-accton-as7315-27xb-cpld.c
new file mode 100755
index 0000000000..dcd604f1f6
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/x86-64-accton-as7315-27xb-cpld.c
@@ -0,0 +1,1008 @@
+/*
+ * A hwmon driver for the as7315_i2c_cpld
+ *
+ * Copyright (C) 2019 Accton Technology Corporation.
+ * Brandon Chuang
+ *
+ * Based on ad7414.c
+ * Copyright 2006 Stefan Roese , DENX Software Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#define DRV_NAME "as7315_i2c_cpld"
+#define NUM_SFP 24
+#define NUM_QSFP 3
+#define NUM_ALL_PORTS 27
+
+#define SFP_1ST_PRST_REG 0x10
+#define QSFP_1ST_PRST_REG 0x18
+
+#define SFP_1ST_RXLOS_REG 0x13
+#define MUX_CHANNEL_SELECT_REG 0x80
+
+
+enum models {
+ MDL_CPLD_SFP,
+ MDL_CPLD_QSFP,
+ PLAIN_CPLD, /*No attribute but add i2c addr to the list.*/
+ NUM_MODEL
+};
+
+
+#define MAX_PORT_NUM 64
+#define I2C_RW_RETRY_COUNT 10
+#define I2C_RW_RETRY_INTERVAL 60 /* ms */
+
+/*
+ * Number of additional attribute pointers to allocate
+ * with each call to krealloc
+ */
+#define ATTR_ALLOC_SIZE 1 /*For last attribute which is NUll.*/
+
+#define NAME_SIZE 24
+
+typedef ssize_t (*show_func)( struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+typedef ssize_t (*store_func)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+enum port_type {
+ PT_SFP,
+ PT_QSFP,
+};
+
+enum common_attrs {
+ CMN_VERSION,
+ CMN_ACCESS,
+ CMN_PRESENT_ALL,
+ CMN_RXLOS_ALL,
+ NUM_COMMON_ATTR
+};
+
+enum sfp_sb_attrs {
+ SFP_PRESENT,
+ SFP_RESET,
+ SFP_TX_DIS,
+ SFP_TX_FAULT,
+ SFP_RX_LOS,
+ QSFP_PRESENT,
+ QSFP_LP_MODE,
+ NUM_SFP_SB_ATTR
+};
+
+struct cpld_sensor {
+ struct cpld_sensor *next;
+ char name[NAME_SIZE+1]; /* sysfs sensor name */
+ struct device_attribute attribute;
+ bool update; /* runtime sensor update needed */
+ int data; /* Sensor data. Negative if there was a read error */
+
+ u8 reg; /* register */
+ u8 mask; /* bit mask */
+ bool invert; /* inverted value*/
+
+};
+
+#define to_cpld_sensor(_attr) \
+ container_of(_attr, struct cpld_sensor, attribute)
+
+struct cpld_data {
+ struct device *dev;
+ struct device *hwmon_dev;
+
+ int num_attributes;
+ struct attribute_group group;
+
+ enum models model;
+ struct cpld_sensor *sensors;
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+
+ int attr_index;
+ u16 sfp_num;
+ u8 sfp_types;
+ struct model_attrs *attrs;
+
+ /*For mux function*/
+ struct i2c_mux_core *muxc;
+};
+
+struct cpld_client_node {
+ struct i2c_client *client;
+ struct list_head list;
+};
+
+
+struct base_attrs {
+ const char *name;
+ umode_t mode;
+ show_func get;
+ store_func set;
+};
+
+struct attrs {
+ int reg;
+ bool invert;
+ struct base_attrs *base;
+};
+
+struct model_attrs {
+ struct attrs **cmn;
+ struct attrs **portly;
+};
+
+
+static ssize_t show_bit(struct device *dev,
+ struct device_attribute *devattr, char *buf);
+static ssize_t show_rxlos_all(struct device *dev,
+ struct device_attribute *devattr, char *buf);
+static ssize_t show_presnet_all(struct device *dev,
+ struct device_attribute *devattr, char *buf);
+static ssize_t set_1bit(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count);
+static ssize_t set_byte(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count);
+static ssize_t access(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count);
+
+int accton_i2c_cpld_read(u8 cpld_addr, u8 reg);
+int accton_i2c_cpld_write(u8 cpld_addr, u8 reg, u8 value);
+
+
+struct base_attrs common_base_attrs[NUM_COMMON_ATTR] =
+{
+ [CMN_VERSION] = {"version", S_IRUGO, show_bit, NULL},
+ [CMN_ACCESS] = {"access", S_IWUSR, NULL, set_byte},
+ [CMN_PRESENT_ALL] = {"module_present_all", S_IRUGO, show_presnet_all, NULL},
+ [CMN_RXLOS_ALL] = {"rx_los_all", S_IRUGO, show_rxlos_all, NULL},
+};
+
+struct attrs common_attrs[] = {
+ [CMN_VERSION] = {0x01, false, &common_base_attrs[CMN_VERSION]},
+ [CMN_ACCESS] = {-1, false, &common_base_attrs[CMN_ACCESS]},
+ [CMN_PRESENT_ALL] = {-1, false, &common_base_attrs[CMN_PRESENT_ALL]},
+ [CMN_RXLOS_ALL] = {-1, false, &common_base_attrs[CMN_RXLOS_ALL]},
+};
+struct attrs plain_common[] = {
+ [CMN_VERSION] = {0x01, false, &common_base_attrs[CMN_VERSION]},
+};
+
+struct base_attrs portly_attrs[] =
+{
+ [SFP_PRESENT] = {"present", S_IRUGO, show_bit, NULL},
+ [SFP_RESET] = {0},
+ [SFP_TX_DIS] = {"tx_disable", S_IRUGO|S_IWUSR, show_bit, set_1bit},
+ [SFP_TX_FAULT] = {"tx_fault", S_IRUGO|S_IWUSR, show_bit, set_1bit},
+ [SFP_RX_LOS] = {"rx_los", S_IRUGO|S_IWUSR, show_bit, set_1bit},
+ [QSFP_PRESENT] = {"present", S_IRUGO, show_bit, NULL},
+ [QSFP_LP_MODE] = {"low_power_mode", S_IRUGO|S_IWUSR, show_bit, set_1bit},
+};
+
+struct attrs as7315_port[NUM_SFP_SB_ATTR] = {
+ {SFP_1ST_PRST_REG, true, &portly_attrs[SFP_PRESENT]},
+ {0},
+ {0x12, false, &portly_attrs[SFP_TX_DIS]},
+ {0x11, false, &portly_attrs[SFP_TX_FAULT]},
+ {SFP_1ST_RXLOS_REG, false, &portly_attrs[SFP_RX_LOS]},
+ {QSFP_1ST_PRST_REG, true, &portly_attrs[QSFP_PRESENT]},
+ {0x19, false, &portly_attrs[QSFP_LP_MODE]},
+};
+
+struct attrs *as7315_cmn_list[] = {
+ &common_attrs[CMN_VERSION],
+ &common_attrs[CMN_ACCESS],
+ &common_attrs[CMN_PRESENT_ALL],
+ &common_attrs[CMN_RXLOS_ALL],
+ NULL
+};
+
+struct attrs *plain_cmn_list[] = {
+ &plain_common[CMN_VERSION],
+ NULL
+};
+
+struct attrs *as7315_qsfp_list[] = {
+ &as7315_port[QSFP_PRESENT],
+ &as7315_port[QSFP_LP_MODE],
+ NULL
+};
+
+struct attrs *as7315_sfp_list[] = {
+ &as7315_port[SFP_PRESENT],
+ &as7315_port[SFP_TX_DIS],
+ &as7315_port[SFP_TX_FAULT],
+ &as7315_port[SFP_RX_LOS],
+ NULL
+};
+
+struct model_attrs models_attr[NUM_MODEL] = {
+ {.cmn = as7315_cmn_list, .portly=as7315_sfp_list},
+ {.cmn = as7315_cmn_list, .portly=as7315_qsfp_list},
+ {.cmn = plain_cmn_list, .portly=NULL},
+};
+
+static LIST_HEAD(cpld_client_list);
+static struct mutex list_lock;
+/* Addresses scanned for as7315_i2c_cpld
+ */
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+struct chip_desc {
+ u8 nchans;
+ u8 deselectChan;
+};
+
+/* Provide specs for the PCA954x types we know about */
+static const struct chip_desc chips[] = {
+ [MDL_CPLD_SFP] = {0},
+ [MDL_CPLD_QSFP] = {
+ .nchans = 4, /*Fan + 3*LM75*/
+ .deselectChan = 0,
+ },
+};
+
+static const struct i2c_device_id as7315_cpld_id[] = {
+ { "as7315_cpld1", MDL_CPLD_SFP},
+ { "as7315_cpld2", MDL_CPLD_QSFP},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, as7315_cpld_id);
+
+
+
+
+static int get_sfp_spec(int model, u16 *num, u8 *types)
+{
+ switch (model) {
+ case MDL_CPLD_SFP:
+ *num = NUM_SFP;
+ *types = PT_SFP;
+ break;
+ case MDL_CPLD_QSFP:
+ *num = NUM_QSFP;
+ *types = PT_QSFP;
+ break;
+ default:
+ *types = 0;
+ *num = 0;
+ break;
+ }
+
+ return 0;
+}
+
+/*num: how many bits can be applied for this read.*/
+static int get_present_reg(int model, u8 port, u8 *reg, u8 *num)
+{
+
+ switch (model) {
+ case MDL_CPLD_SFP:
+ if (port < NUM_SFP) {
+ *reg = SFP_1ST_PRST_REG + (port/8)*4;
+ *num = (NUM_SFP > 8)? 8 : NUM_SFP;
+ return 0;
+ }
+ break;
+ case MDL_CPLD_QSFP:
+ if (port < NUM_QSFP) {
+ *reg = QSFP_1ST_PRST_REG + (port/8);
+ *num = (NUM_QSFP > 8)? 8 : NUM_QSFP;
+ return 0;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int get_rxlos_reg(int model, u8 port, u8 *reg, u8 *num)
+{
+
+ switch (model) {
+ case MDL_CPLD_SFP:
+ if (port < NUM_SFP) {
+ *reg = SFP_1ST_RXLOS_REG + (port/8)*4;
+ *num = (NUM_SFP > 8)? 8 : NUM_SFP;
+ return 0;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int cpld_write_internal(
+ struct i2c_client *client, u8 reg, u8 value)
+{
+ int status = 0, retry = I2C_RW_RETRY_COUNT;
+
+ while (retry) {
+ status = i2c_smbus_write_byte_data(client, reg, value);
+ if (unlikely(status < 0)) {
+ msleep(I2C_RW_RETRY_INTERVAL);
+ retry--;
+ continue;
+ }
+ break;
+ }
+ return status;
+}
+
+static int cpld_read_internal(struct i2c_client *client, u8 reg)
+{
+ int status = 0, retry = I2C_RW_RETRY_COUNT;
+
+ while (retry) {
+ status = i2c_smbus_read_byte_data(client, reg);
+ if (unlikely(status < 0)) {
+ msleep(I2C_RW_RETRY_INTERVAL);
+ retry--;
+ continue;
+ }
+ break;
+ }
+ return status;
+}
+
+static ssize_t show_rxlos_all(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cpld_data *data = i2c_get_clientdata(client);
+ u8 i, value, reg, num;
+ u64 values;
+
+ i = num = reg =0;
+ values = 0;
+ mutex_lock(&data->update_lock);
+ while (i < data->sfp_num)
+ {
+ /*No rxlos for QSFP*/
+ if (data->model == MDL_CPLD_QSFP) {
+ values = 0;
+ break;
+ }
+ get_rxlos_reg(data->model, i, ®, &num);
+ if (!(num > 0))
+ {
+ value = -EINVAL;
+ goto exit;
+ }
+ value = cpld_read_internal(client, reg);
+ if (unlikely(value < 0)) {
+ goto exit;
+ }
+ values |= (value &((1<<(num))-1)) << i;
+ i += num;
+ }
+ mutex_unlock(&data->update_lock);
+ values = cpu_to_le64(values);
+ return snprintf(buf, sizeof(values)+1, "%llx\n", values);
+exit:
+ mutex_unlock(&data->update_lock);
+ return value;
+}
+
+
+static ssize_t show_presnet_all(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cpld_data *data = i2c_get_clientdata(client);
+ u8 i, value, reg, num;
+ u64 values;
+
+ i = num = reg =0;
+ values = 0;
+ mutex_lock(&data->update_lock);
+ while (i < data->sfp_num)
+ {
+ get_present_reg(data->model, i, ®, &num);
+ if (!(num > 0))
+ {
+ value = -EINVAL;
+ goto exit;
+ }
+ value = cpld_read_internal(client, reg);
+ if (unlikely(value < 0)) {
+ goto exit;
+ }
+ values |= (~value&((1<<(num))-1)) << i;
+ i += num;
+ }
+ mutex_unlock(&data->update_lock);
+ values = cpu_to_le64(values);
+ return snprintf(buf, sizeof(values)+1, "%llx\n", values);
+exit:
+ mutex_unlock(&data->update_lock);
+ return value;
+}
+
+static ssize_t show_bit(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int value;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cpld_data *data = i2c_get_clientdata(client);
+ struct cpld_sensor *sensor = to_cpld_sensor(devattr);
+
+ mutex_lock(&data->update_lock);
+ value = cpld_read_internal(client, sensor->reg);
+ value = value & sensor->mask;
+ if (sensor->invert)
+ value = !value;
+ mutex_unlock(&data->update_lock);
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", !!value);
+}
+
+static ssize_t set_1bit(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ long is_reset;
+ int value, status;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cpld_data *data = i2c_get_clientdata(client);
+ struct cpld_sensor *sensor = to_cpld_sensor(devattr);
+ u8 cpld_bit, reg;
+
+ status = kstrtol(buf, 10, &is_reset);
+ if (status) {
+ return status;
+ }
+ reg = sensor->reg;
+ cpld_bit = sensor->mask;
+ mutex_lock(&data->update_lock);
+ value = cpld_read_internal(client, reg);
+ if (unlikely(status < 0)) {
+ goto exit;
+ }
+
+ if (sensor->invert)
+ is_reset = !is_reset;
+
+ if (is_reset) {
+ value |= cpld_bit;
+ }
+ else {
+ value &= ~cpld_bit;
+ }
+
+ status = cpld_write_internal(client, reg, value);
+ if (unlikely(status < 0)) {
+ goto exit;
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+
+exit:
+ mutex_unlock(&data->update_lock);
+ return status;
+}
+
+static ssize_t set_byte(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ return access(dev, da, buf, count);
+}
+
+static ssize_t access(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ int status;
+ u32 addr, val;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cpld_data *data = i2c_get_clientdata(client);
+
+ if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) {
+ return -EINVAL;
+ }
+
+ if (addr > 0xFF || val > 0xFF) {
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+ status = cpld_write_internal(client, addr, val);
+ if (unlikely(status < 0)) {
+ goto exit;
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+
+exit:
+ mutex_unlock(&data->update_lock);
+ return status;
+}
+
+static void accton_i2c_cpld_add_client(struct i2c_client *client)
+{
+ struct cpld_client_node *node =
+ kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL);
+
+ if (!node) {
+ dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n",
+ client->addr);
+ return;
+ }
+ node->client = client;
+
+ mutex_lock(&list_lock);
+ list_add(&node->list, &cpld_client_list);
+ mutex_unlock(&list_lock);
+}
+
+static void accton_i2c_cpld_remove_client(struct i2c_client *client)
+{
+ struct list_head *list_node = NULL;
+ struct cpld_client_node *cpld_node = NULL;
+ int found = 0;
+
+ mutex_lock(&list_lock);
+
+ list_for_each(list_node, &cpld_client_list)
+ {
+ cpld_node = list_entry(list_node, struct cpld_client_node, list);
+
+ if (cpld_node->client == client) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ list_del(list_node);
+ kfree(cpld_node);
+ }
+
+ mutex_unlock(&list_lock);
+}
+
+static int cpld_add_attribute(struct cpld_data *data, struct attribute *attr)
+{
+ int new_max_attrs = ++data->num_attributes + ATTR_ALLOC_SIZE;
+ void *new_attrs = krealloc(data->group.attrs,
+ new_max_attrs * sizeof(void *),
+ GFP_KERNEL);
+ if (!new_attrs)
+ return -ENOMEM;
+ data->group.attrs = new_attrs;
+ data->group.attrs[data->num_attributes-1] = attr;
+ data->group.attrs[data->num_attributes] = NULL;
+
+ return 0;
+}
+
+static void cpld_dev_attr_init(struct device_attribute *dev_attr,
+ const char *name, umode_t mode,
+ show_func show, store_func store)
+{
+ sysfs_attr_init(&dev_attr->attr);
+ dev_attr->attr.name = name;
+ dev_attr->attr.mode = mode;
+ dev_attr->show = show;
+ dev_attr->store = store;
+}
+
+static struct cpld_sensor * add_sensor(struct cpld_data *data,
+ const char *name,
+ u8 reg, u8 mask, bool invert,
+ bool update, umode_t mode,
+ show_func get, store_func set)
+{
+ struct cpld_sensor *sensor;
+ struct device_attribute *a;
+
+ sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return NULL;
+ a = &sensor->attribute;
+
+ snprintf(sensor->name, sizeof(sensor->name), name);
+ sensor->reg = reg;
+ sensor->mask = mask;
+ sensor->update = update;
+ sensor->invert = invert;
+ cpld_dev_attr_init(a, sensor->name,
+ mode,
+ get, set);
+
+ if (cpld_add_attribute(data, &a->attr))
+ return NULL;
+
+ sensor->next = data->sensors;
+ data->sensors = sensor;
+
+ return sensor;
+}
+
+static int add_attributes_cmn(struct cpld_data *data, struct attrs **cmn)
+{
+ u8 reg, i ;
+ bool invert;
+ struct attrs *a;
+ struct base_attrs *b;
+
+ if (NULL == cmn)
+ return -1;
+
+ for (i = 0; cmn[i]; i++)
+ {
+ a = cmn[i];
+
+ reg = a->reg;
+ invert = a->invert;
+
+ b = a->base;
+ if (NULL == b)
+ break;
+
+ if (add_sensor(data, b->name,
+ reg, 0xff, invert,
+ true, b->mode,
+ b->get, b->set) == NULL)
+ {
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+static int add_attributes_portly(struct cpld_data *data, struct attrs **pa)
+{
+ char name[NAME_SIZE+1];
+ int i, j;
+ u8 reg, mask, invert, reg_start, jump;
+ struct attrs *a;
+ struct base_attrs *b;
+
+ if (NULL == pa)
+ return -EFAULT;
+
+ jump = 0;
+ if (data->sfp_types == PT_SFP) {
+ jump = 4; /*SFP sideband registers in set of 4 bytes.*/
+ } else if (data->sfp_types == PT_QSFP) {
+ jump = 1;
+ }
+
+ for (i = 0; pa[i]; i++) {
+ a = pa[i];
+ reg_start = a->reg;
+ invert = a->invert;
+ b = a->base;
+ if (b == NULL)
+ break;
+
+ for (j = 0; j < data->sfp_num; j++)
+ {
+ snprintf(name, NAME_SIZE, "%s_%d", b->name, j+1);
+ reg = reg_start + ((j/8)*jump);
+ mask = 1 << ((j)%8);
+ if (add_sensor(data, name, reg, mask, invert,
+ true, b->mode, b->get, b->set) == NULL)
+ {
+ return -ENOMEM;
+ }
+ }
+ }
+
+
+ return 0;
+}
+
+static int add_attributes(struct i2c_client *client,
+ struct cpld_data *data)
+{
+ struct model_attrs *m = data->attrs;
+
+ if (m == NULL)
+ return -EINVAL;
+
+ /* Common attributes.*/
+ add_attributes_cmn(data, m->cmn);
+
+ /* Port-wise attributes.*/
+ add_attributes_portly(data, m->portly);
+
+ return 0;
+}
+
+/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
+ for this as they will try to lock adapter a second time */
+static int _cpld_mux_reg_write(struct i2c_adapter *adap,
+ struct i2c_client *client, u8 val)
+{
+ unsigned long orig_jiffies;
+ unsigned short flags;
+ union i2c_smbus_data data;
+ int try;
+ s32 res = -EIO;
+
+ data.byte = val;
+ flags = client->flags;
+ flags &= ~(I2C_M_TEN | I2C_CLIENT_PEC);
+
+ if (adap->algo->smbus_xfer) {
+ /* Retry automatically on arbitration loss */
+ orig_jiffies = jiffies;
+ for (res = 0, try = 0; try <= adap->retries; try++) {
+ res = adap->algo->smbus_xfer(adap, client->addr, flags,
+ I2C_SMBUS_WRITE, MUX_CHANNEL_SELECT_REG,
+ I2C_SMBUS_BYTE_DATA, &data);
+ if (res != -EAGAIN)
+ break;
+ if (time_after(jiffies,
+ orig_jiffies + adap->timeout))
+ break;
+ }
+ }
+
+ return res;
+}
+
+
+static int _mux_select_chan(struct i2c_mux_core *muxc,
+ u32 chan)
+{
+ u8 regval;
+ struct i2c_client *client = i2c_mux_priv(muxc);
+ int ret = 0;
+
+ regval = (chan+1) << 5;
+ ret = _cpld_mux_reg_write(muxc->parent, client, regval);
+ if (unlikely(ret < 0)) {
+ return ret;
+ }
+ return ret;
+}
+
+
+static int _prealloc_attrs(struct cpld_data *data)
+{
+ void *new_attrs = krealloc(data->group.attrs,
+ ATTR_ALLOC_SIZE * sizeof(void *),
+ GFP_KERNEL);
+ if (!new_attrs)
+ return -ENOMEM;
+ data->group.attrs = new_attrs;
+
+ return 0;
+}
+
+static int _add_sysfs_attributes(struct i2c_client *client,
+ struct cpld_data *data)
+{
+ int status;
+
+ status = add_attributes(client, data);
+ if (status) {
+ return status;
+ }
+
+ /*If there are no attributes, something is wrong.
+ * Bail out instead of trying to register nothing.
+ */
+ if (!data->num_attributes) {
+ dev_err(&client->dev, "No attributes found\n");
+ return -ENODEV;
+ }
+
+ /* Register sysfs hooks */
+ status = sysfs_create_group(&client->dev.kobj, &data->group);
+ if (status) {
+ return status;
+ }
+
+ return 0;
+}
+
+
+static int _add_mux_channels(struct i2c_client *client,
+ const struct i2c_device_id *id, struct cpld_data *data)
+{
+ int num, force, class;
+ int status;
+ struct i2c_mux_core *muxc;
+ struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
+ int model = id->driver_data;
+
+ data->muxc = i2c_mux_alloc(adap, &client->dev,
+ chips[model].nchans,
+ sizeof(client), 0,
+ _mux_select_chan, NULL);
+
+ if (!data->muxc) {
+ return ENOMEM;
+ }
+ muxc = data->muxc;
+ muxc->priv = client;
+ for (num = 0; num < chips[model].nchans; num++) {
+ force = 0; /* dynamic adap number */
+ class = 0; /* no class by default */
+ status = i2c_mux_add_adapter(muxc, force, num, class);
+ if (status)
+ return status ;
+ }
+ dev_info(&client->dev,
+ "registered %d multiplexed busses for I2C %s\n",
+ num, client->name);
+
+ return 0;
+}
+
+static int as7315_i2c_cpld_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
+ int status;
+ struct cpld_data *data = NULL;
+ struct device *dev = &client->dev;
+
+ if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ return -ENOMEM;
+ }
+ data->model = dev_id->driver_data;
+ data->attrs = &models_attr[data->model];
+ get_sfp_spec(data->model, &data->sfp_num, &data->sfp_types);
+ mutex_init(&data->update_lock);
+ data->dev = dev;
+ dev_info(dev, "chip found\n");
+
+
+ status = _prealloc_attrs(data);
+ if (status)
+ return -ENOMEM;
+
+ status = _add_sysfs_attributes(client, data);
+ if (status)
+ goto out_kfree;
+
+ status = _add_mux_channels(client, dev_id, data);
+ if (status)
+ goto exit_rm_sys;
+
+ i2c_set_clientdata(client, data);
+ accton_i2c_cpld_add_client(client);
+ dev_info(dev, "%s: cpld '%s'\n",
+ dev_name(data->dev), client->name);
+
+ return 0;
+
+exit_rm_sys:
+ sysfs_remove_group(&client->dev.kobj, &data->group);
+out_kfree:
+ kfree(data->group.attrs);
+ return status;
+}
+
+
+static int as7315_i2c_cpld_remove(struct i2c_client *client)
+{
+ struct cpld_data *data = i2c_get_clientdata(client);
+ struct i2c_mux_core *muxc = data->muxc;
+
+ sysfs_remove_group(&client->dev.kobj, &data->group);
+ kfree(data->group.attrs);
+ if(muxc) {
+ i2c_mux_del_adapters(muxc);
+ }
+ accton_i2c_cpld_remove_client(client);
+ return 0;
+}
+
+int accton_i2c_cpld_read(u8 cpld_addr, u8 reg)
+{
+ struct list_head *list_node = NULL;
+ struct cpld_client_node *cpld_node = NULL;
+ int ret = -EPERM;
+
+ mutex_lock(&list_lock);
+
+ list_for_each(list_node, &cpld_client_list)
+ {
+ cpld_node = list_entry(list_node, struct cpld_client_node, list);
+
+ if (cpld_node->client->addr == cpld_addr) {
+ ret = i2c_smbus_read_byte_data(cpld_node->client, reg);
+ break;
+ }
+ }
+
+ mutex_unlock(&list_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(accton_i2c_cpld_read);
+
+int accton_i2c_cpld_write(u8 cpld_addr, u8 reg, u8 value)
+{
+ struct list_head *list_node = NULL;
+ struct cpld_client_node *cpld_node = NULL;
+ int ret = -EIO;
+
+ mutex_lock(&list_lock);
+
+ list_for_each(list_node, &cpld_client_list)
+ {
+ cpld_node = list_entry(list_node, struct cpld_client_node, list);
+
+ if (cpld_node->client->addr == cpld_addr) {
+ ret = i2c_smbus_write_byte_data(cpld_node->client, reg, value);
+ break;
+ }
+ }
+
+ mutex_unlock(&list_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(accton_i2c_cpld_write);
+
+
+static struct i2c_driver as7315_i2c_cpld_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = as7315_i2c_cpld_probe,
+ .remove = as7315_i2c_cpld_remove,
+ .id_table = as7315_cpld_id,
+ .address_list = normal_i2c,
+};
+
+
+static int __init as7315_i2c_cpld_init(void)
+{
+ mutex_init(&list_lock);
+ return i2c_add_driver(&as7315_i2c_cpld_driver);
+}
+
+static void __exit as7315_i2c_cpld_exit(void)
+{
+ i2c_del_driver(&as7315_i2c_cpld_driver);
+}
+
+module_init(as7315_i2c_cpld_init);
+module_exit(as7315_i2c_cpld_exit);
+
+MODULE_AUTHOR("Roy Lee ");
+MODULE_DESCRIPTION("as7315_i2c_cpld driver");
+MODULE_LICENSE("GPL");
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/x86-64-accton-as7315-27xb-led.c b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/x86-64-accton-as7315-27xb-led.c
new file mode 100755
index 0000000000..fefb44fad3
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/x86-64-accton-as7315-27xb-led.c
@@ -0,0 +1,408 @@
+/*
+ * A LED driver for the accton_as7315_27xb_led
+ *
+ * Copyright (C) 2019 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
+
+#define DRVNAME "as7315_27xb_led"
+#define CPLD_I2C_ADDR 0x64
+
+enum led_type {
+ TYPE_DIAG,
+ TYPE_LOC,
+ TYPE_MAX
+};
+
+struct led_list_s {
+ enum led_type type;
+ char name[64];
+ u8 reg_addr;
+ u8 slave_addr;
+} led_list[TYPE_MAX] = {
+ {TYPE_DIAG, "as7315_27xb_diag", 0x41, CPLD_I2C_ADDR},
+ {TYPE_LOC, "as7315_27xb_loc", 0x40, CPLD_I2C_ADDR}
+};
+
+struct accton_as7315_27xb_led_data {
+ struct platform_device *pdev;
+ struct mutex update_lock;
+ char valid; /* != 0 if registers are valid */
+ unsigned long last_updated; /* In jiffies */
+ u8 slave_addr[TYPE_MAX]; /* For LOC and DIAG.*/
+ u8 reg_addr[TYPE_MAX]; /* For LOC and DIAG.*/
+ u8 reg_val[TYPE_MAX]; /* Register value, 0 = LOC
+ 1 = DIAG */
+};
+static struct accton_as7315_27xb_led_data *ledctl = NULL;
+
+/* LED related data
+ */
+#define TYPE_DIAG_REG_MASK 0x30
+#define MODE_DIAG_GREEN_MASK 0x10
+#define MODE_DIAG_AMBER_MASK 0x20
+#define MODE_DIAG_GBLINK_MASK 0x00
+#define MODE_DIAG_OFF_MASK 0x30
+
+#define TYPE_LOC_REG_MASK 0x40
+#define MODE_LOC_OFF_MASK 0x40
+#define MODE_LOC_BLINK_MASK 0x00
+
+
+typedef enum onlp_led_mode_e {
+ ONLP_LED_MODE_OFF,
+ ONLP_LED_MODE_ON,
+ ONLP_LED_MODE_BLINKING,
+ ONLP_LED_MODE_RED = 10,
+ ONLP_LED_MODE_RED_BLINKING = 11,
+ ONLP_LED_MODE_ORANGE = 12,
+ ONLP_LED_MODE_ORANGE_BLINKING = 13,
+ ONLP_LED_MODE_YELLOW = 14,
+ ONLP_LED_MODE_YELLOW_BLINKING = 15,
+ ONLP_LED_MODE_GREEN = 16,
+ ONLP_LED_MODE_GREEN_BLINKING = 17,
+ ONLP_LED_MODE_BLUE = 18,
+ ONLP_LED_MODE_BLUE_BLINKING = 19,
+ ONLP_LED_MODE_PURPLE = 20,
+ ONLP_LED_MODE_PURPLE_BLINKING = 21,
+ ONLP_LED_MODE_AUTO = 22,
+ ONLP_LED_MODE_AUTO_BLINKING = 23,
+} onlp_led_mode_t;
+
+
+struct led_type_mode {
+ enum led_type type;
+ int type_mask;
+ enum onlp_led_mode_e mode;
+ int mode_mask;
+};
+
+static struct led_type_mode led_type_mode_data[] = {
+ {TYPE_DIAG, TYPE_DIAG_REG_MASK, ONLP_LED_MODE_GREEN_BLINKING, MODE_DIAG_GBLINK_MASK},
+ {TYPE_DIAG, TYPE_DIAG_REG_MASK, ONLP_LED_MODE_GREEN, MODE_DIAG_GREEN_MASK},
+ {TYPE_DIAG, TYPE_DIAG_REG_MASK, ONLP_LED_MODE_ORANGE, MODE_DIAG_AMBER_MASK},
+ {TYPE_DIAG, TYPE_DIAG_REG_MASK, ONLP_LED_MODE_OFF, MODE_DIAG_OFF_MASK},
+ {TYPE_LOC, TYPE_LOC_REG_MASK, ONLP_LED_MODE_BLUE_BLINKING, MODE_LOC_BLINK_MASK},
+ {TYPE_LOC, TYPE_LOC_REG_MASK, ONLP_LED_MODE_OFF, MODE_LOC_OFF_MASK},
+};
+
+extern int accton_i2c_cpld_read (u8 cpld_addr, u8 reg);
+extern int accton_i2c_cpld_write(u8 cpld_addr, u8 reg, u8 value);
+
+
+static int pdata_init(struct accton_as7315_27xb_led_data *data) {
+ int i;
+
+ for (i=0; ireg_addr[i] = led_list[i].reg_addr;
+ data->slave_addr[i] = led_list[i].slave_addr;
+ }
+
+ return 0;
+}
+
+static int led_reg_val_to_light_mode(enum led_type type, u8 reg_val) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(led_type_mode_data); i++) {
+
+ if (type != led_type_mode_data[i].type)
+ continue;
+
+ if ((led_type_mode_data[i].type_mask & reg_val) ==
+ led_type_mode_data[i].mode_mask)
+ {
+ return led_type_mode_data[i].mode;
+ }
+ }
+
+ return 0;
+}
+
+static u8 led_light_mode_to_reg_val(enum led_type type,
+ enum onlp_led_mode_e mode, u8 reg_val) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(led_type_mode_data); i++) {
+ if (type != led_type_mode_data[i].type)
+ continue;
+
+ if (mode != led_type_mode_data[i].mode)
+ continue;
+
+ reg_val = led_type_mode_data[i].mode_mask |
+ (reg_val & (~led_type_mode_data[i].type_mask));
+ }
+
+ return reg_val;
+}
+
+static int accton_as7315_27xb_led_read_value(u8 slave, u8 reg)
+{
+ return accton_i2c_cpld_read(slave, reg);
+}
+
+static int accton_as7315_27xb_led_write_value(u8 slave, u8 reg, u8 value)
+{
+ return accton_i2c_cpld_write(slave, reg, value);
+}
+
+static void accton_as7315_27xb_led_update(void)
+{
+ mutex_lock(&ledctl->update_lock);
+
+ if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2)
+ || !ledctl->valid) {
+ int i;
+
+ dev_dbg(&ledctl->pdev->dev, "Starting accton_as7315_27xb_led update\n");
+
+ /* Update LED data
+ */
+ for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) {
+ int status;
+ u8 addr, offset;
+ addr = ledctl->slave_addr[i];
+ offset = ledctl->reg_addr[i];
+ status = accton_as7315_27xb_led_read_value(addr, offset);
+ if (status < 0) {
+ ledctl->valid = 0;
+ dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", ledctl->reg_addr[i], status);
+ goto exit;
+ }
+ else
+ {
+ ledctl->reg_val[i] = status;
+ }
+ }
+
+ ledctl->last_updated = jiffies;
+ ledctl->valid = 1;
+ }
+
+exit:
+ mutex_unlock(&ledctl->update_lock);
+}
+
+static void accton_as7315_27xb_led_set(struct led_classdev *led_cdev,
+ enum led_brightness led_light_mode,
+ enum led_type type)
+{
+ int reg_val;
+ u8 addr, offset;
+
+ if (type >= ARRAY_SIZE(led_list)) {
+ dev_dbg(&ledctl->pdev->dev, "Illegal type:%d\n", type);
+ return;
+ }
+ mutex_lock(&ledctl->update_lock);
+ addr = ledctl->slave_addr[type],
+ offset = ledctl->reg_addr[type],
+ reg_val = accton_as7315_27xb_led_read_value(addr, offset);
+ if (reg_val < 0) {
+ dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", offset, reg_val);
+ goto exit;
+ }
+
+ reg_val = led_light_mode_to_reg_val(type, led_light_mode, reg_val);
+ accton_as7315_27xb_led_write_value(addr, offset, reg_val);
+
+ /* to prevent the slow-update issue */
+ ledctl->valid = 0;
+
+exit:
+ mutex_unlock(&ledctl->update_lock);
+}
+
+static enum led_type get_led_type(struct led_classdev *cdev)
+{
+ int i;
+
+ for (i=0; iname))
+ return i;
+ }
+ return -EINVAL;
+}
+
+static void led_mode_set(struct led_classdev *cdev,
+ enum led_brightness led_light_mode)
+{
+ enum led_type type;
+
+ type = get_led_type(cdev);
+ if (type < 0) {
+ dev_dbg(&ledctl->pdev->dev, "Found no corresponding type:%d\n", type);
+ return ;
+ }
+ accton_as7315_27xb_led_set(cdev, led_light_mode, type);
+}
+
+static enum led_brightness led_mode_get(struct led_classdev *cdev)
+{
+ enum led_type type;
+
+ type = get_led_type(cdev);
+ if (type < 0)
+ return type;
+
+ accton_as7315_27xb_led_update();
+ return led_reg_val_to_light_mode(type, ledctl->reg_val[type]);
+}
+
+static struct led_classdev accton_as7315_27xb_leds[TYPE_MAX] = {
+ [TYPE_DIAG] = {
+ .name = led_list[TYPE_DIAG].name,
+ .default_trigger = "unused",
+ .brightness_set = led_mode_set,
+ .brightness_get = led_mode_get,
+ .flags = LED_CORE_SUSPENDRESUME,
+ .max_brightness = ONLP_LED_MODE_AUTO,
+ },
+ [TYPE_LOC] = {
+ .name = led_list[TYPE_LOC].name,
+ .default_trigger = "unused",
+ .brightness_set = led_mode_set,
+ .brightness_get = led_mode_get,
+ .flags = LED_CORE_SUSPENDRESUME,
+ .max_brightness = ONLP_LED_MODE_AUTO,
+ },
+};
+
+static int accton_as7315_27xb_led_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(accton_as7315_27xb_leds); i++) {
+ led_classdev_suspend(&accton_as7315_27xb_leds[i]);
+ }
+
+ return 0;
+}
+
+static int accton_as7315_27xb_led_resume(struct platform_device *dev)
+{
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(accton_as7315_27xb_leds); i++) {
+ led_classdev_resume(&accton_as7315_27xb_leds[i]);
+ }
+
+ return 0;
+}
+
+static int accton_as7315_27xb_led_probe(struct platform_device *pdev)
+{
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(accton_as7315_27xb_leds); i++) {
+ ret = led_classdev_register(&pdev->dev, &accton_as7315_27xb_leds[i]);
+
+ if (ret < 0)
+ break;
+ }
+
+ /* Check if all LEDs were successfully registered */
+ if (i != ARRAY_SIZE(accton_as7315_27xb_leds)) {
+ int j;
+
+ /* only unregister the LEDs that were successfully registered */
+ for (j = 0; j < i; j++) {
+ led_classdev_unregister(&accton_as7315_27xb_leds[i]);
+ }
+ }
+
+ return ret;
+}
+
+static int accton_as7315_27xb_led_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(accton_as7315_27xb_leds); i++) {
+ led_classdev_unregister(&accton_as7315_27xb_leds[i]);
+ }
+
+ return 0;
+}
+
+static struct platform_driver accton_as7315_27xb_led_driver = {
+ .probe = accton_as7315_27xb_led_probe,
+ .remove = accton_as7315_27xb_led_remove,
+ .suspend = accton_as7315_27xb_led_suspend,
+ .resume = accton_as7315_27xb_led_resume,
+ .driver = {
+ .name = DRVNAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init accton_as7315_27xb_led_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&accton_as7315_27xb_led_driver);
+ if (ret < 0) {
+ goto exit;
+ }
+
+ ledctl = kzalloc(sizeof(struct accton_as7315_27xb_led_data), GFP_KERNEL);
+ if (!ledctl) {
+ ret = -ENOMEM;
+ platform_driver_unregister(&accton_as7315_27xb_led_driver);
+ goto exit;
+ }
+
+ pdata_init(ledctl);
+ 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_as7315_27xb_led_driver);
+ kfree(ledctl);
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+static void __exit accton_as7315_27xb_led_exit(void)
+{
+ platform_device_unregister(ledctl->pdev);
+ platform_driver_unregister(&accton_as7315_27xb_led_driver);
+ kfree(ledctl);
+}
+
+module_init(accton_as7315_27xb_led_init);
+module_exit(accton_as7315_27xb_led_exit);
+
+MODULE_AUTHOR("Brandon Chuang ");
+MODULE_DESCRIPTION("accton_as7315_27xb_led driver");
+MODULE_LICENSE("GPL");
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/x86-64-accton-as7315-27xb-psu.c b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/x86-64-accton-as7315-27xb-psu.c
new file mode 100644
index 0000000000..1cf6313eab
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/x86-64-accton-as7315-27xb-psu.c
@@ -0,0 +1,454 @@
+/*
+ * An hwmon driver for accton as7315_27xb Power Module
+ *
+ * Copyright (C) 2019 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
+
+#define DRV_NAME "as7315_27xb_psu"
+#define PSU_STATUS_I2C_ADDR 0x64
+#define PSU_STATUS_I2C_REG_OFFSET 0x2
+#define USE_BYTE_ACCESS 0 /*Somehow i2c block access is failed on this platform.*/
+#define UPDATE_PERIOD (HZ*2)
+#define MAX_OUTPUT_LENGTH 32
+#define BASIC_EEPROM_SIZE 32
+
+#define IS_POWER_GOOD(id, value) (!!(value & BIT(id + 2)))
+#define IS_PRESENT(id, value) (!(value & BIT(id)))
+
+/* Addresses scanned
+ */
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* Each client has this additional data
+ */
+struct as7315_27xb_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 eeprom[BASIC_EEPROM_SIZE*2]; /* EEPROM*/
+};
+
+
+enum as7315_27xb_psu_sysfs_attributes {
+ PSU_INDEX,
+ PSU_PRESENT,
+ PSU_MODEL_NAME,
+ PSU_POWER_GOOD,
+ PSU_SERIAL_NUMBER
+};
+
+enum psu_type {
+ PSU_YM_1401_A, /* AC110V - B2F */
+ PSU_YM_2401_JCR, /* AC110V - F2B */
+ PSU_YM_2401_JDR, /* AC110V - B2F */
+ PSU_YM_2401_TCR, /* AC110V - B2F */
+ PSU_CPR_4011_4M11, /* AC110V - F2B */
+ PSU_CPR_4011_4M21, /* AC110V - B2F */
+ PSU_CPR_6011_2M11, /* AC110V - F2B */
+ PSU_CPR_6011_2M21, /* AC110V - B2F */
+ PSU_UM400D_01G, /* DC48V - F2B */
+ PSU_UM400D01_01G, /* DC48V - B2F */
+ PSU_BEL_TOT120, /* DC48V - N/A */
+ PSU_TYPE_MAX
+};
+
+struct model_info {
+ enum psu_type type;
+ u8 offset;
+ char* model_name;
+ u8 serial_offset;
+};
+
+struct model_info models[] = {
+ {PSU_YM_1401_A, 0x20, "YM-1401ACR",0x35},
+ {PSU_YM_2401_JCR, 0x20, "YM-2401JCR",0x35},
+ {PSU_YM_2401_JDR, 0x20, "YM-2401JDR",0x35},
+ {PSU_YM_2401_TCR, 0x20, "YM-2401TCR",0x35},
+ {PSU_CPR_4011_4M11, 0x26, "CPR-4011-4M11",0x47},
+ {PSU_CPR_4011_4M21, 0x26, "CPR-4011-4M21",0x47},
+ {PSU_CPR_6011_2M11, 0x26, "CPR-6011-2M11",0x46},
+ {PSU_CPR_6011_2M21, 0x26, "CPR-6011-2M21",0x46},
+ {PSU_UM400D_01G, 0x50, "um400d01G",0x50},
+ {PSU_UM400D01_01G, 0x50, "um400d01-01G",0x50},
+ {PSU_BEL_TOT120, 0x0A, "CRXT-T0T120",0x18},
+};
+
+static ssize_t show_index(struct device *dev, struct device_attribute *da, char *buf);
+static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf);
+static ssize_t show_model_name(struct device *dev, struct device_attribute *da, char *buf);
+static ssize_t show_serial_number(struct device *dev, struct device_attribute *da,char *buf);
+static int as7315_27xb_psu_block_read(struct i2c_client *client, u8 command, u8 *data,int data_len);
+static int as7315_27xb_psu_model_name_get(
+ struct device *dev, char *buf);
+static int as7315_27xb_psu_serial_number_get(
+ struct device *dev, enum psu_type type, char *out);
+static struct as7315_27xb_psu_data *as7315_27xb_psu_update_device(struct device *dev);
+extern int accton_i2c_cpld_read(u8 cpld_addr, u8 reg);
+
+/* sysfs attributes for hwmon
+ */
+static SENSOR_DEVICE_ATTR(psu_index, S_IRUGO, show_index, NULL, PSU_INDEX);
+static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_status, NULL, PSU_PRESENT);
+static SENSOR_DEVICE_ATTR(psu_model_name, S_IRUGO, show_model_name,NULL, PSU_MODEL_NAME);
+static SENSOR_DEVICE_ATTR(psu_serial, S_IRUGO, show_serial_number, NULL, PSU_SERIAL_NUMBER);
+static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD);
+
+static struct attribute *as7315_27xb_psu_attributes[] = {
+ &sensor_dev_attr_psu_index.dev_attr.attr,
+ &sensor_dev_attr_psu_present.dev_attr.attr,
+ &sensor_dev_attr_psu_model_name.dev_attr.attr,
+ &sensor_dev_attr_psu_serial.dev_attr.attr,
+ &sensor_dev_attr_psu_power_good.dev_attr.attr,
+ NULL
+};
+
+static ssize_t show_index(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct as7315_27xb_psu_data *data = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%d\n", data->index);
+}
+
+static ssize_t show_status(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct as7315_27xb_psu_data *data = as7315_27xb_psu_update_device(dev);
+ u8 status = 0;
+
+ if (!data->valid) {
+ return sprintf(buf, "0\n");
+ }
+
+ if (attr->index == PSU_PRESENT) {
+ status = IS_PRESENT(data->index, data->status);
+ }
+ else { /* PSU_POWER_GOOD */
+ status = IS_POWER_GOOD(data->index, data->status);
+ }
+
+ return sprintf(buf, "%d\n", status);
+}
+
+static ssize_t show_serial_number(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ int i;
+ struct as7315_27xb_psu_data *data = as7315_27xb_psu_update_device(dev);
+
+ if (!data->valid) {
+ return 0;
+ }
+
+ if (!IS_PRESENT(data->index, data->status)) {
+ return 0;
+ }
+
+ i = as7315_27xb_psu_model_name_get(dev, buf);
+ if ( i < 0) {
+ return -ENXIO;
+ }
+
+ if (as7315_27xb_psu_serial_number_get(dev, i, buf) < 0) {
+ return -ENXIO;
+ }
+ return sprintf(buf, "%s\n", buf);
+}
+
+static ssize_t show_model_name(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct as7315_27xb_psu_data *data = as7315_27xb_psu_update_device(dev);
+
+ if (!data->valid) {
+ return 0;
+ }
+
+ if (!IS_PRESENT(data->index, data->status)) {
+ return 0;
+ }
+
+ if (as7315_27xb_psu_model_name_get(dev, buf) < 0) {
+ return -ENXIO;
+ }
+
+ return sprintf(buf, "%s\n", buf);
+}
+
+static const struct attribute_group as7315_27xb_psu_group = {
+ .attrs = as7315_27xb_psu_attributes,
+};
+
+static int as7315_27xb_psu_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ struct as7315_27xb_psu_data *data;
+ int status;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ status = -EIO;
+ goto exit;
+ }
+
+ data = kzalloc(sizeof(struct as7315_27xb_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, &as7315_27xb_psu_group);
+ if (status) {
+ goto exit_free;
+ }
+ data->hwmon_dev = hwmon_device_register_with_info(&client->dev,
+ client->name, NULL, NULL, NULL);
+ 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, &as7315_27xb_psu_group);
+exit_free:
+ kfree(data);
+exit:
+
+ return status;
+}
+
+static int as7315_27xb_psu_remove(struct i2c_client *client)
+{
+ struct as7315_27xb_psu_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &as7315_27xb_psu_group);
+ kfree(data);
+
+ return 0;
+}
+
+enum psu_index
+{
+ as7315_27xb_psu1,
+ as7315_27xb_psu2
+};
+
+static const struct i2c_device_id as7315_27xb_psu_id[] = {
+ { "as7315_27xb_psu1", as7315_27xb_psu1 },
+ { "as7315_27xb_psu2", as7315_27xb_psu2 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, as7315_27xb_psu_id);
+
+static struct i2c_driver as7315_27xb_psu_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = as7315_27xb_psu_probe,
+ .remove = as7315_27xb_psu_remove,
+ .id_table = as7315_27xb_psu_id,
+ .address_list = normal_i2c,
+};
+
+static int as7315_27xb_psu_block_read(struct i2c_client *client,
+ u8 command, u8 *data, int max_len)
+{
+ int result;
+
+ u8 i, offset;
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ for (i = 0; i < max_len; i += 32) {
+ offset = i + command ;
+ result = i2c_smbus_read_i2c_block_data(client, offset,
+ 32, data + i);
+
+ if (result != 32) {
+ result = -EIO;
+ goto abort;
+ }
+ }
+
+ } else {
+ for (i = 0; i < max_len; i += 2) {
+ int word;
+ offset = i + command ;
+ word = i2c_smbus_read_word_data(client, offset);
+ if (word < 0) {
+ result = -EIO;
+ goto abort;
+ }
+ data[i] = word & 0xff;
+ data[i + 1] = word >> 8;
+ }
+ }
+ result = 0;
+abort:
+ return result;
+}
+
+static int as7315_27xb_psu_serial_number_get(
+ struct device *dev, enum psu_type type, char *out)
+{
+ char *serial;
+ struct as7315_27xb_psu_data *data = as7315_27xb_psu_update_device(dev);
+
+ if (type >= PSU_TYPE_MAX) {
+ return -EINVAL;
+ }
+
+ if (!data->valid) {
+ out[0] = '\0';
+ return 0;
+ }
+
+ serial = data->eeprom + models[type].serial_offset;
+ strncpy(out, serial, MAX_OUTPUT_LENGTH);
+ return 0;
+}
+
+static int find_model_name_from_eeprom( char *eeprom)
+{
+ int i;
+ char *name;
+
+ for (i = 0; i < ARRAY_SIZE(models); i++) {
+ name = eeprom + models[i].offset;
+ if (strncmp(name, models[i].model_name,
+ strlen(models[i].model_name)) == 0) {
+ break;
+ }
+ }
+
+ return (i == ARRAY_SIZE(models))? -EINVAL: i;
+}
+static int as7315_27xb_psu_model_name_get(
+ struct device *dev, char *buf)
+{
+ int i;
+ struct as7315_27xb_psu_data *data = as7315_27xb_psu_update_device(dev);
+
+ if (!data->valid) {
+ return sprintf(buf, "0\n");
+ }
+
+ /* Determine if the model name is known, if not, read next index
+ */
+ i = find_model_name_from_eeprom(data->eeprom);
+ if (i < 0) {
+ return -ENODATA;
+ }
+
+ mutex_lock(&data->update_lock);
+ strncpy(buf, models[i].model_name, MAX_OUTPUT_LENGTH);
+ /*Work-around for some special models*/
+ if (i == PSU_YM_2401_JCR || i == PSU_YM_2401_JDR ||
+ i == PSU_YM_1401_A || i == PSU_YM_2401_TCR) {
+ /* Skip the meaningless data byte 8*/
+ buf[8] = buf[9];
+ buf[9] = buf[10];
+ buf = '\0';
+ }
+
+ mutex_unlock(&data->update_lock);
+ return i;
+}
+
+static struct as7315_27xb_psu_data *as7315_27xb_psu_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct as7315_27xb_psu_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+ if (time_after(jiffies, data->last_updated + UPDATE_PERIOD)
+ || !data->valid) {
+ int status = -1;
+
+ dev_dbg(&client->dev, "Starting as7315_27xb update\n");
+ data->valid = 0;
+
+ /* Read psu status */
+ status = accton_i2c_cpld_read(PSU_STATUS_I2C_ADDR, PSU_STATUS_I2C_REG_OFFSET);
+ if (status < 0) {
+ dev_dbg(&client->dev,
+ "cpld reg (0x%x) err %d\n", PSU_STATUS_I2C_ADDR, status);
+ goto exit;
+ }
+ else {
+ data->status = status;
+ }
+
+
+ /*Read the eeprom of psu*/
+ memset(data->eeprom, 0, sizeof(data->eeprom));
+ status = as7315_27xb_psu_block_read(client, 0,
+ data->eeprom, sizeof(data->eeprom));
+
+ if (status < 0) {
+ dev_dbg(&client->dev, "unable to read eeprom from (0x%x)\n",
+ client->addr);
+ goto exit;
+ }
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+exit:
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+module_i2c_driver(as7315_27xb_psu_driver);
+
+MODULE_AUTHOR("Brandon Chuang ");
+MODULE_DESCRIPTION("accton as7315_27xb_psu driver");
+MODULE_LICENSE("GPL");
+
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/ym2651y.c b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/modules/ym2651y.c
new file mode 120000
index 0000000000..f4d67640cc
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/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/as7315-27xb/service/as7315-platform-init.service b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/service/as7315-platform-init.service
new file mode 100755
index 0000000000..cdaf437166
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/service/as7315-platform-init.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Accton AS7315-27XB Platform initialization service
+Before=pmon.service
+After=sysinit.target
+DefaultDependencies=no
+
+[Service]
+ExecStartPre=/usr/local/bin/accton_as7315_util.py install
+ExecStart=/usr/local/bin/accton_as7315_monitor.py
+RemainAfterExit=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/setup.py b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/setup.py
new file mode 100755
index 0000000000..09187900c7
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/setup.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+import os
+import sys
+from setuptools import setup
+os.listdir
+
+setup(
+ name='as7315_27xb',
+ version='1.0',
+ description='Module to initialize Accton AS7315-27XB platforms',
+
+ packages=['as7315_27xb'],
+ package_dir={'as7315_27xb': 'as7315-27xb/classes'},
+)
+
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/utils/README b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/utils/README
new file mode 100755
index 0000000000..0561fe7a27
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/utils/README
@@ -0,0 +1,60 @@
+Copyright (C) 2019 Accton Networks, Inc.
+
+This program is free software: you can redistribute it and/or modify
+It under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+To initialize the system, run "accton_as7315_util.py install".
+To clean up the drivers & devices, run "accton_as7315_util.py clean".
+To dump information of sensors, run "accton_as7315_util.py show".
+To dump SFP EEPROM, run "accton_as7315_util.py sff".
+To set fan speed, run "accton_as7315_util.py set fan".
+To enable/disable SFP emission, run "accton_as7315_util.py set sfp".
+To set system LEDs' color, run "accton_as7315_util.py set led"
+For more information, run "accton_as7315_util.py --help".
+
+====================================================================
+Besides applying accton_as7315_util.py to access peripherals, you can
+access peripherals by sysfs nodes directly after the installation is run.
+
+LED controls can be found under /sys/class/leds. The sysfs interface
+color mappings are as follows:
+Brightness:
+ 0 => off
+ 1 => green
+ 2 => amber
+ 3 => red
+ 4 => blue
+
+There are 5 system LEDs, loc, diag, fan, ps1, and ps2.
+They are lit automatically by CPLD, but the loc and diag.
+The loc led has only 1 color, blue.
+The diag one has 3 colors: red, amber, and green.
+
+Fan controls can be found in /sys/bus/i2c/devices/2-0066.
+There are 10 fans inside 5 fan modules.
+All fans share 1 duty setting, ranged from 0~100.
+
+Three temperature sensors are controlled by the lm75 kernel modules.
+They should already be visible under /sys/bus/i2c/drivers/lm75/.
+
+Two power supplies are controlled by the CPLD.
+Here provide their status under
+/sys/bus/i2c/devices/10-0050 and /sys/bus/i2c/devices/11-0053.
+
+There are QSFP/SFP modules are equipped.
+Apply "accton_as7315_util.py show" to get their status.
+Apply "accton_as7315_util.py set sfp" to turn on/off light transmission.
+Apply "accton_as7315_util.py sff" to dump EEPROM information.
+Before operating on that QSFP+, please make sure it is well plugged.
+Otherwise, operation is going to fail.
+
diff --git a/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/utils/accton_as7315_monitor.py b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/utils/accton_as7315_monitor.py
new file mode 100755
index 0000000000..5d821350ac
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/utils/accton_as7315_monitor.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2019 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
+# 8/02/2019: Roy Lee modify for as7315_27x
+# ------------------------------------------------------------------
+
+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 as7315_27xb.fanutil import FanUtil
+ from as7315_27xb.thermalutil import ThermalUtil
+except ImportError as e:
+ raise ImportError('%s - required module not found' % str(e))
+
+# Deafults
+VERSION = '1.0'
+FUNCTION_NAME = 'accton_as7315_monitor'
+DUTY_MAX = 100
+DUTY_DEF = 40
+
+global log_file
+global log_console
+
+
+# Make a class we can use to capture stdout and sterr in the log
+class accton_as7315_monitor(object):
+ # static temp var
+ _ori_temp = 0
+ _new_perc = 0
+ _ori_perc = 0
+
+ llog = logging.getLogger("["+FUNCTION_NAME+"]")
+ def __init__(self, log_console, log_file):
+ """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(logging.WARNING) #only fatal for syslog
+ self.llog.addHandler(sys_handler)
+ self.llog.setLevel(logging.DEBUG)
+
+ if log_file:
+ fh = logging.FileHandler(log_file)
+ fh.setLevel(logging.INFO)
+ formatter = logging.Formatter('%(asctime)-15s %(name)s %(message)s')
+ fh.setFormatter(formatter)
+ self.llog.addHandler(fh)
+
+ # set up logging to console
+ if log_console:
+ console = logging.StreamHandler()
+ console.setLevel(logging.DEBUG) #For debugging
+ 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 = {
+ 0: [52, 0, 43000],
+ 1: [63, 43000, 46000],
+ 2: [75, 46000, 52000],
+ 3: [88, 52000, 57000],
+ 4: [max_duty, 57000, sys.maxsize],
+ }
+
+ 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.debug('SET new_perc to %d (FAN fault. fan_num:%d)', max_duty, x)
+ fan.set_fan_duty_cycle(max_duty)
+ return True
+
+ #Find if current duty matched any of define duty.
+ #If not, set it to highest one.
+ cur_duty_cycle = fan.get_fan_duty_cycle()
+ new_duty_cycle = DUTY_DEF
+ 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()
+ 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)
+
+ 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_file = '%s.log' % FUNCTION_NAME
+ log_console = 0
+ log_file = ""
+ if len(sys.argv) != 1:
+ try:
+ opts, args = getopt.getopt(argv,'hdl')
+ except getopt.GetoptError:
+ print 'Usage: %s [-d] [-l]' % sys.argv[0]
+ return 0
+ for opt, arg in opts:
+ if opt == '-h':
+ print 'Usage: %s [-d] [-l]' % sys.argv[0]
+ return 0
+ elif opt in ('-d'):
+ 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_as7315_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/as7315-27xb/utils/accton_as7315_util.py b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/utils/accton_as7315_util.py
new file mode 100755
index 0000000000..ea856e4ff5
--- /dev/null
+++ b/platform/broadcom/sonic-platform-modules-accton/as7315-27xb/utils/accton_as7315_util.py
@@ -0,0 +1,535 @@
+#!/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 = 'as7315_27xb'
+version = '0.1.0'
+verbose = False
+DEBUG = False
+args = []
+ALL_DEVICE = {}
+DEVICE_NO = {'led':2, 'fan':5,'thermal':3, 'psu':2, 'sfp':27}
+FORCE = 0
+#logging.basicConfig(filename= PROJECT_NAME+'.log', filemode='w',level=logging.DEBUG)
+#logging.basicConfig(level=logging.INFO)
+
+qsfp_start_index = 24
+
+
+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-1 \" to set led color"
+ print " use \""+ cmd + " fan 0-100\" to set fan duty percetage"
+ print " use \""+ cmd + " sfp 1-27 {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-27 \" 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 optoe',
+'modprobe ym2651y',
+'modprobe accton_as7315_27xb_fan',
+'modprobe at24_as7315_27xb',
+'modprobe x86-64-accton-as7315_27xb-cpld',
+'modprobe x86-64-accton-as7315_27xb-led',
+'modprobe x86-64-accton-as7315_27xb-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)):
+ #remove parameter if any
+ rm = kos[-(i+1)]
+ lst = rm.split(" ")
+ if len(lst) > 2:
+ del(lst[2:])
+ rm = " ".join(lst)
+
+ #Change to removing commands
+ rm = rm.replace("modprobe", "modprobe -rq")
+ rm = rm.replace("insmod", "rmmod")
+ status, output = log_os_system(rm, 1)
+ if status:
+ if FORCE == 0:
+ return status
+ return 0
+
+led_prefix ='/sys/class/leds/'+PROJECT_NAME+'_'
+hwmon_types = {'led': ['diag','loc']}
+hwmon_nodes = {'led': ['brightness'] }
+hwmon_prefix ={'led': led_prefix}
+
+i2c_prefix = '/sys/bus/i2c/devices/'
+i2c_bus = {'fan': ['50-0066'] ,
+ 'thermal': ['51-0049','52-004a', '53-004c'] ,
+ 'psu': ['13-0053','12-0050'],
+ 'sfp': ['-0050']}
+i2c_nodes = {'fan': ['present', 'input'] ,
+ 'thermal': ['hwmon/hwmon*/temp1_input'] ,
+ 'psu': ['psu_present', 'psu_power_good'] ,
+ 'sfp': ['present']}
+
+sfp_map = [26,27,28,29,30,31,32,33,
+ 34,35,36,37,38,39,40,41,
+ 42,43,44,45,46,47,48,49,
+ 21, 22, 23]
+
+mknod =[
+'echo pca9548 0x76 > /sys/bus/i2c/devices/i2c-0/new_device',
+'echo pca9548 0x74 > /sys/bus/i2c/devices/i2c-3/new_device',
+'echo pca9548 0x72 > /sys/bus/i2c/devices/i2c-1/new_device',
+'echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-18/new_device',
+'echo pca9548 0x71 > /sys/bus/i2c/devices/i2c-19/new_device',
+'echo pca9548 0x73 > /sys/bus/i2c/devices/i2c-20/new_device',
+
+'echo as7315_cpld1 0x63 > /sys/bus/i2c/devices/i2c-8/new_device',
+'echo as7315_cpld2 0x64 > /sys/bus/i2c/devices/i2c-7/new_device',
+
+'echo 24cxb04 0x57 > /sys/bus/i2c/devices/i2c-4/new_device',
+'echo as7315_27xb_psu2 0x50 > /sys/bus/i2c/devices/i2c-12/new_device',
+'echo ym2401 0x58 > /sys/bus/i2c/devices/i2c-12/new_device',
+'echo as7315_27xb_psu1 0x53 > /sys/bus/i2c/devices/i2c-13/new_device',
+'echo ym2401 0x5b > /sys/bus/i2c/devices/i2c-13/new_device',
+'echo as7315_fan 0x66 > /sys/bus/i2c/devices/i2c-50/new_device',
+'echo lm75 0x49 > /sys/bus/i2c/devices/i2c-51/new_device',
+'echo lm75 0x4a > /sys/bus/i2c/devices/i2c-52/new_device',
+'echo lm75 0x4c > /sys/bus/i2c/devices/i2c-53/new_device',
+]
+
+def i2c_order_check():
+ return 0
+
+def device_install():
+ global FORCE
+
+ for i in range(0,len(mknod)):
+ #for pca954x need times to built new i2c buses
+ if mknod[i].find('pca954') != -1:
+ time.sleep(1)
+
+ status, output = log_os_system(mknod[i], 1)
+ if status:
+ print output
+ if FORCE == 0:
+ return status
+
+ for i in range(0,len(sfp_map)):
+ path = "/sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/new_device"
+ if 1 >= qsfp_start_index:
+ status, output =log_os_system("echo optoe1 0x50 > " + path, 1)
+ else:
+ status, output =log_os_system("echo optoe2 0x50 > " + path, 1)
+ if status:
+ print output
+ if FORCE == 0:
+ return status
+ return
+
+def device_uninstall():
+ global FORCE
+
+ status, output =log_os_system("ls /sys/bus/i2c/devices/1-0076", 0)
+
+ for i in range(0,len(sfp_map)):
+ target = "/sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/delete_device"
+ status, output =log_os_system("echo 0x50 > "+ target, 1)
+ if status:
+ print output
+ if FORCE == 0:
+ return status
+
+ nodelist = mknod
+
+ for i in range(len(nodelist)):
+ target = nodelist[-(i+1)]
+ temp = target.split()
+ del temp[1]
+ temp[-1] = temp[-1].replace('new_device', 'delete_device')
+ status, output = log_os_system(" ".join(temp), 1)
+ if status:
+ print output
+ if FORCE == 0:
+ return status
+
+ return
+
+def system_ready():
+ if driver_check() == False:
+ return False
+ if not device_exist():
+ return False
+ return True
+
+def do_install():
+ print "Checking system...."
+ if driver_check() == False:
+ print "No driver, installing...."
+ status = driver_install()
+ if status:
+ if FORCE == 0:
+ return status
+ else:
+ print PROJECT_NAME.upper()+" drivers detected...."
+
+ if not device_exist():
+ print "No device, installing...."
+ status = device_install()
+ if status:
+ if FORCE == 0:
+ return status
+ else:
+ print PROJECT_NAME.upper()+" devices detected...."
+ return
+
+def do_uninstall():
+ print "Checking system...."
+ if not device_exist():
+ print PROJECT_NAME.upper() +" has no device installed...."
+ else:
+ print "Removing device...."
+ status = device_uninstall()
+ if status:
+ if FORCE == 0:
+ return status
+
+ if driver_check()== False :
+ print PROJECT_NAME.upper() +" has no driver installed...."
+ else:
+ print "Removing installed driver...."
+ status = driver_uninstall()
+ if status:
+ if FORCE == 0:
+ return status
+
+ return
+
+def devices_info():
+ global DEVICE_NO
+ global ALL_DEVICE
+ global i2c_bus, hwmon_types
+ for key in DEVICE_NO:
+ ALL_DEVICE[key]= {}
+ for i in range(0,DEVICE_NO[key]):
+ ALL_DEVICE[key][key+str(i+1)] = []
+
+ for key in i2c_bus:
+ buses = i2c_bus[key]
+ nodes = i2c_nodes[key]
+ for i in range(0,len(buses)):
+ for j in range(0,len(nodes)):
+ if 'fan' == key:
+ for k in range(0,DEVICE_NO[key]):
+ node = key+str(k+1)
+ path = i2c_prefix+ buses[i]+"/fan"+str(k+1)+"_"+ nodes[j]
+ my_log(node+": "+ path)
+ ALL_DEVICE[key][node].append(path)
+ elif 'sfp' == key:
+ for k in range(0,DEVICE_NO[key]):
+ node = key+str(k+1)
+ if k > qsfp_start_index:
+ fmt = i2c_prefix+"7-0064/{0}_{1}"
+ else:
+ fmt = i2c_prefix+"8-0063/{0}_{1}"
+ path = fmt.format(nodes[j], (k%qsfp_start_index)+1)
+
+ my_log(node+": "+ path)
+ ALL_DEVICE[key][node].append(path)
+ else:
+ node = key+str(i+1)
+ path = i2c_prefix+ buses[i]+"/"+ nodes[j]
+ my_log(node+": "+ path)
+ ALL_DEVICE[key][node].append(path)
+
+ for key in hwmon_types:
+ itypes = hwmon_types[key]
+ nodes = hwmon_nodes[key]
+ for i in range(0,len(itypes)):
+ for j in range(0,len(nodes)):
+ node = key+"_"+itypes[i]
+ path = hwmon_prefix[key]+ itypes[i]+"/"+ nodes[j]
+ my_log(node+": "+ path)
+ ALL_DEVICE[key][ key+str(i+1)].append(path)
+
+ #show dict all in the order
+ if DEBUG == True:
+ for i in sorted(ALL_DEVICE.keys()):
+ print(i+": ")
+ for j in sorted(ALL_DEVICE[i].keys()):
+ print(" "+j)
+ for k in (ALL_DEVICE[i][j]):
+ print(" "+" "+k)
+ return
+
+def show_eeprom(index):
+ if system_ready()==False:
+ print("System's not ready.")
+ print("Please install first!")
+ return
+
+ i = int(index)-1
+ node = i2c_prefix+ str(sfp_map[i])+ i2c_bus['sfp'][0]+"/"+ '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(hex_cmd +" -C "+node, 1)
+ if ret==0:
+ print log
+ else:
+ print "**********device no found**********"
+ return
+
+def set_device(args):
+ global DEVICE_NO
+ global ALL_DEVICE
+ if system_ready()==False:
+ print("System's not ready.")
+ print("Please install first!")
+ return
+
+ if len(ALL_DEVICE)==0:
+ devices_info()
+
+ if args[0]=='led':
+ if int(args[1])>4:
+ show_set_help()
+ return
+ #print ALL_DEVICE['led']
+ for i in range(0,len(ALL_DEVICE['led'])):
+ for k in (ALL_DEVICE['led']['led'+str(i+1)]):
+ ret, log = log_os_system("echo "+args[1]+" >"+k, 1)
+ if ret:
+ return ret
+ elif args[0]=='fan':
+ if int(args[1])>100:
+ show_set_help()
+ return
+ #print ALL_DEVICE['fan']
+ #fan1~6 is all fine, all fan share same setting
+ node = ALL_DEVICE['fan'] ['fan1'][0]
+ node = node.replace(node.split("/")[-1], 'fan_duty_cycle_percentage')
+ ret, log = log_os_system("cat "+ node, 1)
+ if ret==0:
+ print ("Previous fan duty: " + log.strip() +"%")
+ ret, log = log_os_system("echo "+args[1]+" >"+node, 1)
+ if ret==0:
+ print ("Current fan duty: " + args[1] +"%")
+ return ret
+ elif args[0]=='sfp':
+ if int(args[1])> DEVICE_NO[args[0]] or int(args[1])==0:
+ show_set_help()
+ return
+ if len(args)<2:
+ show_set_help()
+ return
+
+ if int(args[2])>1:
+ show_set_help()
+ return
+
+ #print ALL_DEVICE[args[0]]
+ for i in range(0,len(ALL_DEVICE[args[0]])):
+ for j in ALL_DEVICE[args[0]][args[0]+str(args[1])]:
+ if j.find('tx_disable')!= -1:
+ ret, log = log_os_system("echo "+args[2]+" >"+ j, 1)
+ if ret:
+ return ret
+
+ return
+
+#get digits inside a string.
+#Ex: 31 for "sfp31"
+def get_value(input):
+ digit = re.findall('\d+', input)
+ return int(digit[0])
+
+def device_traversal():
+ if system_ready()==False:
+ print("System's not ready.")
+ print("Please install first!")
+ return
+
+ if len(ALL_DEVICE)==0:
+ devices_info()
+ for i in sorted(ALL_DEVICE.keys()):
+ print("============================================")
+ print(i.upper()+": ")
+ print("============================================")
+
+ for j in sorted(ALL_DEVICE[i].keys(), key=get_value):
+ print " "+j+":",
+ for k in (ALL_DEVICE[i][j]):
+ ret, log = log_os_system("cat "+k, 0)
+ func = k.split("/")[-1].strip()
+ func = re.sub(j+'_','',func,1)
+ func = re.sub(i.lower()+'_','',func,1)
+ if ret==0:
+ print func+"="+log+" ",
+ else:
+ print func+"="+"X"+" ",
+ print
+ print("----------------------------------------------------------------")
+
+
+ print
+ return
+
+def device_exist():
+ ret1, log = log_os_system("ls "+i2c_prefix+"*0076", 0)
+ ret2, log = log_os_system("ls "+i2c_prefix+"i2c-2", 0)
+ return not(ret1 or ret2)
+
+if __name__ == "__main__":
+ main()
diff --git a/platform/broadcom/sonic-platform-modules-accton/debian/control b/platform/broadcom/sonic-platform-modules-accton/debian/control
index cb78e152ff..e8bedd9081 100755
--- a/platform/broadcom/sonic-platform-modules-accton/debian/control
+++ b/platform/broadcom/sonic-platform-modules-accton/debian/control
@@ -68,3 +68,7 @@ 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
+
+Package: sonic-platform-accton-as7315-27xb
+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 e2c6a4df8f..11e5b10ae4 100755
--- a/platform/broadcom/sonic-platform-modules-accton/debian/rules
+++ b/platform/broadcom/sonic-platform-modules-accton/debian/rules
@@ -21,7 +21,7 @@ KERNEL_SRC := /lib/modules/$(KVERSION)
MOD_SRC_DIR:= $(shell pwd)
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_DIRS += as5835-54x as9716-32d as5835-54t as7312-54xs as7315-27xb
MODULE_DIR := modules
UTILS_DIR := utils
SERVICE_DIR := service