From e43d4454e143128a897046a764d7bc9fce83a30c Mon Sep 17 00:00:00 2001 From: Kantaphon Kovathana Date: Thu, 18 Mar 2021 14:47:16 +0700 Subject: [PATCH 1/4] [device/celestica] add seastone2 device --- .../Seastone_2/port_config.ini | 33 + .../Seastone_2/sai.profile | 2 + .../td3-seastone_2-32x100G.config.bcm | 598 ++++++++++++++++++ .../x86_64-cel_seastone_2-r0/custom.bin | Bin 0 -> 264 bytes .../x86_64-cel_seastone_2-r0/default_sku | 1 + .../x86_64-cel_seastone_2-r0/installer.conf | 3 + .../led_proc_init.soc | 9 + .../x86_64-cel_seastone_2-r0/linkscan_fw.bin | Bin 0 -> 4716 bytes .../plugins/eeprom.py | 20 + .../plugins/psuutil.py | 87 +++ .../plugins/sfputil.py | 179 ++++++ .../pmon_daemon_control.json | 7 + .../sonic_platform_config/chassis.json | 45 ++ .../sonic_platform_config/component.json | 62 ++ .../sonic_platform_config/event.py | 112 ++++ .../sonic_platform_config/fan.json | 200 ++++++ .../sonic_platform_config/psu.json | 135 ++++ .../sonic_platform_config/sfp.json | 106 ++++ .../sonic_platform_config/thermal.json | 105 +++ .../sonic_platform_config/watchdog.py | 191 ++++++ 20 files changed, 1895 insertions(+) create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/port_config.ini create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/sai.profile create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/td3-seastone_2-32x100G.config.bcm create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/custom.bin create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/default_sku create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/installer.conf create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/led_proc_init.soc create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/linkscan_fw.bin create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/plugins/eeprom.py create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/plugins/psuutil.py create mode 100755 device/celestica/x86_64-cel_seastone_2-r0/plugins/sfputil.py create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/pmon_daemon_control.json create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/chassis.json create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/component.json create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/event.py create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/fan.json create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/psu.json create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/sfp.json create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/thermal.json create mode 100644 device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/watchdog.py diff --git a/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/port_config.ini b/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/port_config.ini new file mode 100644 index 000000000000..ed6bcff2331c --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/port_config.ini @@ -0,0 +1,33 @@ +# name lanes alias fec index speed +Ethernet0 1,2,3,4 QSFP1 rs 1 100000 +Ethernet4 5,6,7,8 QSFP2 rs 2 100000 +Ethernet8 9,10,11,12 QSFP3 rs 3 100000 +Ethernet12 13,14,15,16 QSFP4 rs 4 100000 +Ethernet16 17,18,19,20 QSFP5 rs 5 100000 +Ethernet20 21,22,23,24 QSFP6 rs 6 100000 +Ethernet24 25,26,27,28 QSFP7 rs 7 100000 +Ethernet28 29,30,31,32 QSFP8 rs 8 100000 +Ethernet32 33,34,35,36 QSFP9 rs 9 100000 +Ethernet36 37,38,39,40 QSFP10 rs 10 100000 +Ethernet40 41,42,43,44 QSFP11 rs 11 100000 +Ethernet44 45,46,47,48 QSFP12 rs 12 100000 +Ethernet48 49,50,51,52 QSFP13 rs 13 100000 +Ethernet52 53,54,55,56 QSFP14 rs 14 100000 +Ethernet56 57,58,59,60 QSFP15 rs 15 100000 +Ethernet60 61,62,63,64 QSFP16 rs 16 100000 +Ethernet64 65,66,67,68 QSFP17 rs 17 100000 +Ethernet68 69,70,71,72 QSFP18 rs 18 100000 +Ethernet72 73,74,75,76 QSFP19 rs 19 100000 +Ethernet76 77,78,79,80 QSFP20 rs 20 100000 +Ethernet80 81,82,83,84 QSFP21 rs 21 100000 +Ethernet84 85,86,87,88 QSFP22 rs 22 100000 +Ethernet88 89,90,91,92 QSFP23 rs 23 100000 +Ethernet92 93,94,95,96 QSFP24 rs 24 100000 +Ethernet96 97,98,99,100 QSFP25 rs 25 100000 +Ethernet100 101,102,103,104 QSFP26 rs 26 100000 +Ethernet104 105,106,107,108 QSFP27 rs 27 100000 +Ethernet108 109,110,111,112 QSFP28 rs 28 100000 +Ethernet112 113,114,115,116 QSFP29 rs 29 100000 +Ethernet116 117,118,119,120 QSFP30 rs 30 100000 +Ethernet120 121,122,123,124 QSFP31 rs 31 100000 +Ethernet124 125,126,127,128 QSFP32 rs 32 100000 \ No newline at end of file diff --git a/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/sai.profile b/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/sai.profile new file mode 100644 index 000000000000..b57101d114f0 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/sai.profile @@ -0,0 +1,2 @@ +SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/td3-seastone_2-32x100G.config.bcm +SAI_NUM_ECMP_MEMBERS=64 diff --git a/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/td3-seastone_2-32x100G.config.bcm b/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/td3-seastone_2-32x100G.config.bcm new file mode 100644 index 000000000000..62bcdbc58840 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/td3-seastone_2-32x100G.config.bcm @@ -0,0 +1,598 @@ +sai_load_hw_config=/etc/bcm/flex/bcm56870_a0_issu/b870.6.4.1/ +help_cli_enable=1 +ifp_inports_support_enable=1 +ipv6_lpm_128b_enable=0x1 +l2_mem_entries=32768 +l2xmsg_mode=1 +l3_max_ecmp_mode=1 +l3_mem_entries=16384 +lpm_scaling_enable=1 +max_vp_lags=0 +mem_cache_enable=0 +memlist_enable=1 +miim_intr_enable=0 +module_64ports=1 +oversubscribe_mode=1 +parity_enable=0 +serdes_lane_config_dfe=on +#serdes_fec_enable=1 +serdes_if_type_ce=14 +pbmp_gport_stack.0=0x0000000000000000000000000000000000000000000000000000000000000000 +pbmp_xport_xe=0x88888888888888882222222222222222 + +ptp_ts_pll_fref=50000000 +ptp_bs_fref_0=50000000 +ptp_bs_fref_1=50000000 + +portmap_1.0=1:100 +portmap_5.0=5:100 +portmap_9.0=9:100 +portmap_13.0=13:100 +portmap_17.0=17:100 +portmap_21.0=21:100 +portmap_25.0=25:100 +portmap_29.0=29:100 +portmap_33.0=33:100 +portmap_37.0=37:100 +portmap_41.0=41:100 +portmap_45.0=45:100 +portmap_49.0=49:100 +portmap_53.0=53:100 +portmap_57.0=57:100 +portmap_61.0=61:100 +portmap_67.0=65:100 +portmap_71.0=69:100 +portmap_75.0=73:100 +portmap_79.0=77:100 +portmap_83.0=81:100 +portmap_87.0=85:100 +portmap_91.0=89:100 +portmap_95.0=93:100 +portmap_99.0=97:100 +portmap_103.0=101:100 +portmap_107.0=105:100 +portmap_111.0=109:100 +portmap_115.0=113:100 +portmap_119.0=117:100 +portmap_123.0=121:100 +portmap_127.0=125:100 +#portmap_66.0=129:10:m +#portmap_130.0=128:10:m + +#wc0 lane swap +phy_chain_tx_lane_map_physical{1.0}=0x0132 +phy_chain_rx_lane_map_physical{1.0}=0x3210 + +#wc1 lane swap +phy_chain_tx_lane_map_physical{5.0}=0x2301 +phy_chain_rx_lane_map_physical{5.0}=0x2031 + +#wc2 lane swap +phy_chain_tx_lane_map_physical{9.0}=0x0132 +phy_chain_rx_lane_map_physical{9.0}=0x3210 + +#wc3 lane swap +phy_chain_tx_lane_map_physical{13.0}=0x3201 +phy_chain_rx_lane_map_physical{13.0}=0x2031 + +#wc4 lane swap +phy_chain_tx_lane_map_physical{17.0}=0x0123 +phy_chain_rx_lane_map_physical{17.0}=0x3210 + +#wc5 lane swap +phy_chain_tx_lane_map_physical{21.0}=0x2301 +phy_chain_rx_lane_map_physical{21.0}=0x2031 + +#wc6 lane swap +phy_chain_tx_lane_map_physical{25.0}=0x0123 +phy_chain_rx_lane_map_physical{25.0}=0x3210 + +#wc7 lane swap +phy_chain_tx_lane_map_physical{29.0}=0x3201 +phy_chain_rx_lane_map_physical{29.0}=0x2031 + +#wc8 lane swap +phy_chain_tx_lane_map_physical{33.0}=0x0213 +phy_chain_rx_lane_map_physical{33.0}=0x1302 + +#wc9 lane swap +phy_chain_tx_lane_map_physical{37.0}=0x1302 +phy_chain_rx_lane_map_physical{37.0}=0x2031 + +#wc10 lane swap +phy_chain_tx_lane_map_physical{41.0}=0x0231 +phy_chain_rx_lane_map_physical{41.0}=0x3120 + +#wc11 lane swap +phy_chain_tx_lane_map_physical{45.0}=0x1302 +phy_chain_rx_lane_map_physical{45.0}=0x2031 + +#wc12 lane swap +phy_chain_tx_lane_map_physical{49.0}=0x2103 +phy_chain_rx_lane_map_physical{49.0}=0x3120 + +#wc13 lane swap +phy_chain_tx_lane_map_physical{53.0}=0x2301 +phy_chain_rx_lane_map_physical{53.0}=0x2031 + +#wc14 lane swap +phy_chain_tx_lane_map_physical{57.0}=0x0123 +phy_chain_rx_lane_map_physical{57.0}=0x2301 + +#wc15 lane swap +phy_chain_tx_lane_map_physical{61.0}=0x3210 +phy_chain_rx_lane_map_physical{61.0}=0x1032 + +#wc16 lane swap +phy_chain_tx_lane_map_physical{65.0}=0x3210 +phy_chain_rx_lane_map_physical{65.0}=0x1023 + +#wc17 lane swap +phy_chain_tx_lane_map_physical{69.0}=0x0123 +phy_chain_rx_lane_map_physical{69.0}=0x1302 + +#wc18 lane swap +phy_chain_tx_lane_map_physical{73.0}=0x2301 +phy_chain_rx_lane_map_physical{73.0}=0x1032 + +#wc19 lane swap +phy_chain_tx_lane_map_physical{77.0}=0x2013 +phy_chain_rx_lane_map_physical{77.0}=0x3120 + +#wc20 lane swap +phy_chain_tx_lane_map_physical{81.0}=0x1302 +phy_chain_rx_lane_map_physical{81.0}=0x2031 + +#wc21 lane swap +phy_chain_tx_lane_map_physical{85.0}=0x0123 +phy_chain_rx_lane_map_physical{85.0}=0x2130 + +#wc22 lane swap +phy_chain_tx_lane_map_physical{89.0}=0x2301 +phy_chain_rx_lane_map_physical{89.0}=0x2031 + +#wc23 lane swap +phy_chain_tx_lane_map_physical{93.0}=0x0312 +phy_chain_rx_lane_map_physical{93.0}=0x2310 + +#wc24 lane swap +phy_chain_tx_lane_map_physical{97.0}=0x2301 +phy_chain_rx_lane_map_physical{97.0}=0x1032 + +#wc25 lane swap +phy_chain_tx_lane_map_physical{101.0}=0x0123 +phy_chain_rx_lane_map_physical{101.0}=0x3210 + +#wc26 lane swap +phy_chain_tx_lane_map_physical{105.0}=0x2301 +phy_chain_rx_lane_map_physical{105.0}=0x1032 + +#wc27 lane swap +phy_chain_tx_lane_map_physical{109.0}=0x0123 +phy_chain_rx_lane_map_physical{109.0}=0x3210 + +#wc28 lane swap +phy_chain_tx_lane_map_physical{113.0}=0x2301 +phy_chain_rx_lane_map_physical{113.0}=0x2031 + +#wc29 lane swap +phy_chain_tx_lane_map_physical{117.0}=0x0123 +phy_chain_rx_lane_map_physical{117.0}=0x3210 + +#wc30 lane swap +phy_chain_tx_lane_map_physical{121.0}=0x2301 +phy_chain_rx_lane_map_physical{121.0}=0x1032 + +#wc31 lane swap +phy_chain_tx_lane_map_physical{125.0}=0x0123 +phy_chain_rx_lane_map_physical{125.0}=0x3210 + +#MC lane swap +phy_chain_tx_lane_map_physical{129.0}=0x3210 +phy_chain_rx_lane_map_physical{129.0}=0x3210 + + +#wc0 P/N flip +phy_chain_tx_polarity_flip_physical{1.0}=0x0 +phy_chain_rx_polarity_flip_physical{1.0}=0x0 +phy_chain_tx_polarity_flip_physical{2.0}=0x0 +phy_chain_rx_polarity_flip_physical{2.0}=0x1 +phy_chain_tx_polarity_flip_physical{3.0}=0x0 +phy_chain_rx_polarity_flip_physical{3.0}=0x0 +phy_chain_tx_polarity_flip_physical{4.0}=0x1 +phy_chain_rx_polarity_flip_physical{4.0}=0x1 + +#wc1 P/N flip +phy_chain_tx_polarity_flip_physical{5.0}=0x0 +phy_chain_rx_polarity_flip_physical{5.0}=0x0 +phy_chain_tx_polarity_flip_physical{6.0}=0x1 +phy_chain_rx_polarity_flip_physical{6.0}=0x1 +phy_chain_tx_polarity_flip_physical{7.0}=0x0 +phy_chain_rx_polarity_flip_physical{7.0}=0x1 +phy_chain_tx_polarity_flip_physical{8.0}=0x1 +phy_chain_rx_polarity_flip_physical{8.0}=0x1 + +#wc2 P/N flip +phy_chain_tx_polarity_flip_physical{9.0}=0x0 +phy_chain_rx_polarity_flip_physical{9.0}=0x0 +phy_chain_tx_polarity_flip_physical{10.0}=0x0 +phy_chain_rx_polarity_flip_physical{10.0}=0x1 +phy_chain_tx_polarity_flip_physical{11.0}=0x0 +phy_chain_rx_polarity_flip_physical{11.0}=0x0 +phy_chain_tx_polarity_flip_physical{12.0}=0x1 +phy_chain_rx_polarity_flip_physical{12.0}=0x1 + +#wc3 P/N flip +phy_chain_tx_polarity_flip_physical{13.0}=0x0 +phy_chain_rx_polarity_flip_physical{13.0}=0x0 +phy_chain_tx_polarity_flip_physical{14.0}=0x1 +phy_chain_rx_polarity_flip_physical{14.0}=0x1 +phy_chain_tx_polarity_flip_physical{15.0}=0x0 +phy_chain_rx_polarity_flip_physical{15.0}=0x1 +phy_chain_tx_polarity_flip_physical{16.0}=0x0 +phy_chain_rx_polarity_flip_physical{16.0}=0x1 + +#wc4 P/N flip +phy_chain_tx_polarity_flip_physical{17.0}=0x0 +phy_chain_rx_polarity_flip_physical{17.0}=0x0 +phy_chain_tx_polarity_flip_physical{18.0}=0x1 +phy_chain_rx_polarity_flip_physical{18.0}=0x1 +phy_chain_tx_polarity_flip_physical{19.0}=0x0 +phy_chain_rx_polarity_flip_physical{19.0}=0x0 +phy_chain_tx_polarity_flip_physical{20.0}=0x1 +phy_chain_rx_polarity_flip_physical{20.0}=0x1 + +#wc5 P/N flip +phy_chain_tx_polarity_flip_physical{21.0}=0x0 +phy_chain_rx_polarity_flip_physical{21.0}=0x0 +phy_chain_tx_polarity_flip_physical{22.0}=0x1 +phy_chain_rx_polarity_flip_physical{22.0}=0x1 +phy_chain_tx_polarity_flip_physical{23.0}=0x0 +phy_chain_rx_polarity_flip_physical{23.0}=0x1 +phy_chain_tx_polarity_flip_physical{24.0}=0x1 +phy_chain_rx_polarity_flip_physical{24.0}=0x1 + +#wc6 P/N flip +phy_chain_tx_polarity_flip_physical{25.0}=0x0 +phy_chain_rx_polarity_flip_physical{25.0}=0x1 +phy_chain_tx_polarity_flip_physical{26.0}=0x1 +phy_chain_rx_polarity_flip_physical{26.0}=0x0 +phy_chain_tx_polarity_flip_physical{27.0}=0x0 +phy_chain_rx_polarity_flip_physical{27.0}=0x1 +phy_chain_tx_polarity_flip_physical{28.0}=0x1 +phy_chain_rx_polarity_flip_physical{28.0}=0x0 + +#wc7 P/N flip +phy_chain_tx_polarity_flip_physical{29.0}=0x1 +phy_chain_rx_polarity_flip_physical{29.0}=0x1 +phy_chain_tx_polarity_flip_physical{30.0}=0x1 +phy_chain_rx_polarity_flip_physical{30.0}=0x0 +phy_chain_tx_polarity_flip_physical{31.0}=0x0 +phy_chain_rx_polarity_flip_physical{31.0}=0x0 +phy_chain_tx_polarity_flip_physical{32.0}=0x0 +phy_chain_rx_polarity_flip_physical{32.0}=0x0 + +#wc8 P/N flip +phy_chain_tx_polarity_flip_physical{33.0}=0x1 +phy_chain_rx_polarity_flip_physical{33.0}=0x1 +phy_chain_tx_polarity_flip_physical{34.0}=0x0 +phy_chain_rx_polarity_flip_physical{34.0}=0x0 +phy_chain_tx_polarity_flip_physical{35.0}=0x0 +phy_chain_rx_polarity_flip_physical{35.0}=0x0 +phy_chain_tx_polarity_flip_physical{36.0}=0x1 +phy_chain_rx_polarity_flip_physical{36.0}=0x0 + +#wc9 P/N flip +phy_chain_tx_polarity_flip_physical{37.0}=0x1 +phy_chain_rx_polarity_flip_physical{37.0}=0x1 +phy_chain_tx_polarity_flip_physical{38.0}=0x1 +phy_chain_rx_polarity_flip_physical{38.0}=0x0 +phy_chain_tx_polarity_flip_physical{39.0}=0x1 +phy_chain_rx_polarity_flip_physical{39.0}=0x0 +phy_chain_tx_polarity_flip_physical{40.0}=0x0 +phy_chain_rx_polarity_flip_physical{40.0}=0x1 + +#wc10 P/N flip +phy_chain_tx_polarity_flip_physical{41.0}=0x1 +phy_chain_rx_polarity_flip_physical{41.0}=0x1 +phy_chain_tx_polarity_flip_physical{42.0}=0x0 +phy_chain_rx_polarity_flip_physical{42.0}=0x1 +phy_chain_tx_polarity_flip_physical{43.0}=0x1 +phy_chain_rx_polarity_flip_physical{43.0}=0x0 +phy_chain_tx_polarity_flip_physical{44.0}=0x1 +phy_chain_rx_polarity_flip_physical{44.0}=0x1 + +#wc11 P/N flip +phy_chain_tx_polarity_flip_physical{45.0}=0x1 +phy_chain_rx_polarity_flip_physical{45.0}=0x0 +phy_chain_tx_polarity_flip_physical{46.0}=0x1 +phy_chain_rx_polarity_flip_physical{46.0}=0x0 +phy_chain_tx_polarity_flip_physical{47.0}=0x1 +phy_chain_rx_polarity_flip_physical{47.0}=0x1 +phy_chain_tx_polarity_flip_physical{48.0}=0x0 +phy_chain_rx_polarity_flip_physical{48.0}=0x1 + +#wc12 P/N flip +phy_chain_tx_polarity_flip_physical{49.0}=0x1 +phy_chain_rx_polarity_flip_physical{49.0}=0x0 +phy_chain_tx_polarity_flip_physical{50.0}=0x1 +phy_chain_rx_polarity_flip_physical{50.0}=0x0 +phy_chain_tx_polarity_flip_physical{51.0}=0x0 +phy_chain_rx_polarity_flip_physical{51.0}=0x1 +phy_chain_tx_polarity_flip_physical{52.0}=0x1 +phy_chain_rx_polarity_flip_physical{52.0}=0x1 + +#wc13 P/N flip +phy_chain_tx_polarity_flip_physical{53.0}=0x0 +phy_chain_rx_polarity_flip_physical{53.0}=0x0 +phy_chain_tx_polarity_flip_physical{54.0}=0x1 +phy_chain_rx_polarity_flip_physical{54.0}=0x1 +phy_chain_tx_polarity_flip_physical{55.0}=0x0 +phy_chain_rx_polarity_flip_physical{55.0}=0x1 +phy_chain_tx_polarity_flip_physical{56.0}=0x1 +phy_chain_rx_polarity_flip_physical{56.0}=0x1 + +#wc14 P/N flip +phy_chain_tx_polarity_flip_physical{57.0}=0x1 +phy_chain_rx_polarity_flip_physical{57.0}=0x0 +phy_chain_tx_polarity_flip_physical{58.0}=0x1 +phy_chain_rx_polarity_flip_physical{58.0}=0x1 +phy_chain_tx_polarity_flip_physical{59.0}=0x0 +phy_chain_rx_polarity_flip_physical{59.0}=0x0 +phy_chain_tx_polarity_flip_physical{60.0}=0x1 +phy_chain_rx_polarity_flip_physical{60.0}=0x1 + +#wc15 P/N flip +phy_chain_tx_polarity_flip_physical{61.0}=0x0 +phy_chain_rx_polarity_flip_physical{61.0}=0x1 +phy_chain_tx_polarity_flip_physical{62.0}=0x1 +phy_chain_rx_polarity_flip_physical{62.0}=0x0 +phy_chain_tx_polarity_flip_physical{63.0}=0x0 +phy_chain_rx_polarity_flip_physical{63.0}=0x1 +phy_chain_tx_polarity_flip_physical{64.0}=0x0 +phy_chain_rx_polarity_flip_physical{64.0}=0x0 + +#wc16 P/N flip +phy_chain_tx_polarity_flip_physical{65.0}=0x1 +phy_chain_rx_polarity_flip_physical{65.0}=0x0 +phy_chain_tx_polarity_flip_physical{66.0}=0x0 +phy_chain_rx_polarity_flip_physical{66.0}=0x0 +phy_chain_tx_polarity_flip_physical{67.0}=0x1 +phy_chain_rx_polarity_flip_physical{67.0}=0x1 +phy_chain_tx_polarity_flip_physical{68.0}=0x0 +phy_chain_rx_polarity_flip_physical{68.0}=0x0 + +#wc17 P/N flip +phy_chain_tx_polarity_flip_physical{69.0}=0x1 +phy_chain_rx_polarity_flip_physical{69.0}=0x1 +phy_chain_tx_polarity_flip_physical{70.0}=0x0 +phy_chain_rx_polarity_flip_physical{70.0}=0x0 +phy_chain_tx_polarity_flip_physical{71.0}=0x1 +phy_chain_rx_polarity_flip_physical{71.0}=0x0 +phy_chain_tx_polarity_flip_physical{72.0}=0x0 +phy_chain_rx_polarity_flip_physical{72.0}=0x0 + +#wc18 P/N flip +phy_chain_tx_polarity_flip_physical{73.0}=0x0 +phy_chain_rx_polarity_flip_physical{73.0}=0x1 +phy_chain_tx_polarity_flip_physical{74.0}=0x1 +phy_chain_rx_polarity_flip_physical{74.0}=0x0 +phy_chain_tx_polarity_flip_physical{75.0}=0x0 +phy_chain_rx_polarity_flip_physical{75.0}=0x1 +phy_chain_tx_polarity_flip_physical{76.0}=0x1 +phy_chain_rx_polarity_flip_physical{76.0}=0x0 + +#wc19 P/N flip +phy_chain_tx_polarity_flip_physical{77.0}=0x0 +phy_chain_rx_polarity_flip_physical{77.0}=0x0 +phy_chain_tx_polarity_flip_physical{78.0}=0x0 +phy_chain_rx_polarity_flip_physical{78.0}=0x0 +phy_chain_tx_polarity_flip_physical{79.0}=0x1 +phy_chain_rx_polarity_flip_physical{79.0}=0x1 +phy_chain_tx_polarity_flip_physical{80.0}=0x1 +phy_chain_rx_polarity_flip_physical{80.0}=0x1 + +#wc20 P/N flip +phy_chain_tx_polarity_flip_physical{81.0}=0x0 +phy_chain_rx_polarity_flip_physical{81.0}=0x0 +phy_chain_tx_polarity_flip_physical{82.0}=0x0 +phy_chain_rx_polarity_flip_physical{82.0}=0x0 +phy_chain_tx_polarity_flip_physical{83.0}=0x1 +phy_chain_rx_polarity_flip_physical{83.0}=0x1 +phy_chain_tx_polarity_flip_physical{84.0}=0x1 +phy_chain_rx_polarity_flip_physical{84.0}=0x0 + +#wc21 P/N flip +phy_chain_tx_polarity_flip_physical{85.0}=0x1 +phy_chain_rx_polarity_flip_physical{85.0}=0x1 +phy_chain_tx_polarity_flip_physical{86.0}=0x0 +phy_chain_rx_polarity_flip_physical{86.0}=0x1 +phy_chain_tx_polarity_flip_physical{87.0}=0x1 +phy_chain_rx_polarity_flip_physical{87.0}=0x0 +phy_chain_tx_polarity_flip_physical{88.0}=0x0 +phy_chain_rx_polarity_flip_physical{88.0}=0x0 + +#wc22 P/N flip +phy_chain_tx_polarity_flip_physical{89.0}=0x1 +phy_chain_rx_polarity_flip_physical{89.0}=0x0 +phy_chain_tx_polarity_flip_physical{90.0}=0x0 +phy_chain_rx_polarity_flip_physical{90.0}=0x0 +phy_chain_tx_polarity_flip_physical{91.0}=0x1 +phy_chain_rx_polarity_flip_physical{91.0}=0x1 +phy_chain_tx_polarity_flip_physical{92.0}=0x0 +phy_chain_rx_polarity_flip_physical{92.0}=0x1 + +#wc23 P/N flip +phy_chain_tx_polarity_flip_physical{93.0}=0x1 +phy_chain_rx_polarity_flip_physical{93.0}=0x1 +phy_chain_tx_polarity_flip_physical{94.0}=0x1 +phy_chain_rx_polarity_flip_physical{94.0}=0x1 +phy_chain_tx_polarity_flip_physical{95.0}=0x0 +phy_chain_rx_polarity_flip_physical{95.0}=0x0 +phy_chain_tx_polarity_flip_physical{96.0}=0x0 +phy_chain_rx_polarity_flip_physical{96.0}=0x1 + +#wc24 P/N flip +phy_chain_tx_polarity_flip_physical{97.0}=0x1 +phy_chain_rx_polarity_flip_physical{97.0}=0x1 +phy_chain_tx_polarity_flip_physical{98.0}=0x0 +phy_chain_rx_polarity_flip_physical{98.0}=0x0 +phy_chain_tx_polarity_flip_physical{99.0}=0x1 +phy_chain_rx_polarity_flip_physical{99.0}=0x1 +phy_chain_tx_polarity_flip_physical{100.0}=0x0 +phy_chain_rx_polarity_flip_physical{100.0}=0x0 + +#wc25 P/N flip +phy_chain_tx_polarity_flip_physical{101.0}=0x1 +phy_chain_rx_polarity_flip_physical{101.0}=0x0 +phy_chain_tx_polarity_flip_physical{102.0}=0x0 +phy_chain_rx_polarity_flip_physical{102.0}=0x1 +phy_chain_tx_polarity_flip_physical{103.0}=0x1 +phy_chain_rx_polarity_flip_physical{103.0}=0x0 +phy_chain_tx_polarity_flip_physical{104.0}=0x0 +phy_chain_rx_polarity_flip_physical{104.0}=0x0 + +#wc26 P/N flip +phy_chain_tx_polarity_flip_physical{105.0}=0x1 +phy_chain_rx_polarity_flip_physical{105.0}=0x0 +phy_chain_tx_polarity_flip_physical{106.0}=0x0 +phy_chain_rx_polarity_flip_physical{106.0}=0x1 +phy_chain_tx_polarity_flip_physical{107.0}=0x1 +phy_chain_rx_polarity_flip_physical{107.0}=0x0 +phy_chain_tx_polarity_flip_physical{108.0}=0x0 +phy_chain_rx_polarity_flip_physical{108.0}=0x1 + +#wc27 P/N flip +phy_chain_tx_polarity_flip_physical{109.0}=0x1 +phy_chain_rx_polarity_flip_physical{109.0}=0x1 +phy_chain_tx_polarity_flip_physical{110.0}=0x0 +phy_chain_rx_polarity_flip_physical{110.0}=0x0 +phy_chain_tx_polarity_flip_physical{111.0}=0x1 +phy_chain_rx_polarity_flip_physical{111.0}=0x1 +phy_chain_tx_polarity_flip_physical{112.0}=0x0 +phy_chain_rx_polarity_flip_physical{112.0}=0x0 + +#wc28 P/N flip +phy_chain_tx_polarity_flip_physical{113.0}=0x1 +phy_chain_rx_polarity_flip_physical{113.0}=0x1 +phy_chain_tx_polarity_flip_physical{114.0}=0x0 +phy_chain_rx_polarity_flip_physical{114.0}=0x0 +phy_chain_tx_polarity_flip_physical{115.0}=0x1 +phy_chain_rx_polarity_flip_physical{115.0}=0x0 +phy_chain_tx_polarity_flip_physical{116.0}=0x0 +phy_chain_rx_polarity_flip_physical{116.0}=0x0 + +#wc29 P/N flip +phy_chain_tx_polarity_flip_physical{117.0}=0x1 +phy_chain_rx_polarity_flip_physical{117.0}=0x1 +phy_chain_tx_polarity_flip_physical{118.0}=0x0 +phy_chain_rx_polarity_flip_physical{118.0}=0x0 +phy_chain_tx_polarity_flip_physical{119.0}=0x1 +phy_chain_rx_polarity_flip_physical{119.0}=0x1 +phy_chain_tx_polarity_flip_physical{120.0}=0x0 +phy_chain_rx_polarity_flip_physical{120.0}=0x0 + +#wc30 P/N flip +phy_chain_tx_polarity_flip_physical{121.0}=0x1 +phy_chain_rx_polarity_flip_physical{121.0}=0x0 +phy_chain_tx_polarity_flip_physical{122.0}=0x0 +phy_chain_rx_polarity_flip_physical{122.0}=0x1 +phy_chain_tx_polarity_flip_physical{123.0}=0x1 +phy_chain_rx_polarity_flip_physical{123.0}=0x0 +phy_chain_tx_polarity_flip_physical{124.0}=0x0 +phy_chain_rx_polarity_flip_physical{124.0}=0x1 + +#wc31 P/N flip +phy_chain_tx_polarity_flip_physical{125.0}=0x1 +phy_chain_rx_polarity_flip_physical{125.0}=0x1 +phy_chain_tx_polarity_flip_physical{126.0}=0x0 +phy_chain_rx_polarity_flip_physical{126.0}=0x0 +phy_chain_tx_polarity_flip_physical{127.0}=0x1 +phy_chain_rx_polarity_flip_physical{127.0}=0x1 +phy_chain_tx_polarity_flip_physical{128.0}=0x0 +phy_chain_rx_polarity_flip_physical{128.0}=0x0 + +#MC P/N flip +phy_chain_tx_polarity_flip_physical{129.0}=0x0 +phy_chain_rx_polarity_flip_physical{129.0}=0x0 +phy_chain_tx_polarity_flip_physical{130.0}=0x0 +phy_chain_rx_polarity_flip_physical{130.0}=0x0 +phy_chain_tx_polarity_flip_physical{131.0}=0x0 +phy_chain_rx_polarity_flip_physical{131.0}=0x0 +phy_chain_tx_polarity_flip_physical{132.0}=0x0 +phy_chain_rx_polarity_flip_physical{132.0}=0x0 + +dport_map_port_1=1 +dport_map_port_5=2 +dport_map_port_9=3 +dport_map_port_13=4 +dport_map_port_17=5 +dport_map_port_21=6 +dport_map_port_25=7 +dport_map_port_29=8 +dport_map_port_33=9 +dport_map_port_37=10 +dport_map_port_41=11 +dport_map_port_45=12 +dport_map_port_49=13 +dport_map_port_53=14 +dport_map_port_57=15 +dport_map_port_61=16 +dport_map_port_67=17 +dport_map_port_71=18 +dport_map_port_75=19 +dport_map_port_79=20 +dport_map_port_83=21 +dport_map_port_87=22 +dport_map_port_91=23 +dport_map_port_95=24 +dport_map_port_99=25 +dport_map_port_103=26 +dport_map_port_107=27 +dport_map_port_111=28 +dport_map_port_115=29 +dport_map_port_119=30 +dport_map_port_123=31 +dport_map_port_127=32 +#dport_map_port_66=33 +#dport_map_port_130=34 + +# configuration for 100G optical module +serdes_preemphasis_1=0x164608 +serdes_preemphasis_5=0x164608 +serdes_preemphasis_9=0x164608 +serdes_preemphasis_13=0x134908 +serdes_preemphasis_17=0x134908 +serdes_preemphasis_21=0x134908 +serdes_preemphasis_25=0x124a08 +serdes_preemphasis_29=0x124a08 +serdes_preemphasis_33=0x114b08 +serdes_preemphasis_37=0x114b08 +serdes_preemphasis_41=0x0f4d08 +serdes_preemphasis_45=0x0f4d08 +serdes_preemphasis_49=0x0d4f08 +serdes_preemphasis_53=0x0d4f08 +serdes_preemphasis_57=0x0d4f08 +serdes_preemphasis_61=0x0d4f08 +serdes_preemphasis_67=0x0d4f08 +serdes_preemphasis_71=0x0d4f08 +serdes_preemphasis_75=0x0d4f08 +serdes_preemphasis_79=0x0d4f08 +serdes_preemphasis_83=0x0d4f08 +serdes_preemphasis_87=0x0f4d08 +serdes_preemphasis_91=0x0f4d08 +serdes_preemphasis_95=0x0f4d08 +serdes_preemphasis_99=0x114b08 +serdes_preemphasis_103=0x114b08 +serdes_preemphasis_107=0x114b08 +serdes_preemphasis_111=0x124a08 +serdes_preemphasis_115=0x134908 +serdes_preemphasis_119=0x134908 +serdes_preemphasis_123=0x134908 +serdes_preemphasis_127=0x164608 + +reglist_enable=1 +scache_filename=/tmp/scache +schan_intr_enable=0 +stable_size=0x5500000 +tdma_timeout_usec=3000000 diff --git a/device/celestica/x86_64-cel_seastone_2-r0/custom.bin b/device/celestica/x86_64-cel_seastone_2-r0/custom.bin new file mode 100644 index 0000000000000000000000000000000000000000..a9597df309ab08e818a2cf65d3a9950e01ab7891 GIT binary patch literal 264 zcmeycHQp`E&DYJv?ZcMl4U7{Rl$li-6?@z+CJALk zXECs4OpMl%X;9~P;=j<6@hpZ@kL$*huynWO4k>PKZstxb7kz*xnmaLE)REzJV!psI zlR=Z=f{u(^#*|=2;}-3^7aNpkGq7j;j$&4lWMjYDpwyDFD2CN4P_E07NwG_69y{{| z#`*i%8Ll!bfmAwXoC<0J(o7kbfjVS>I+~Q)GBzRUm=Dr1vlFbN5vbB7<53W!;=u%< wUlN^_oD!Loni3BtIw&$GH7hzMH6#O}Lg6J5fx^QK65KF)&IUR_p;c zG+QD$DX}4S9msC&B(x3X6qiSFb0U`AMJIAtvLVfZv7KxaOwZ9da1iBe5~U`VY|EPc zM>1^avbE3k+_`tY|Nrm*zJ?$`I{^4`+>hgL{XB5X=0i9S;|Sa`G_IczD(Osv3tD$m zxPi2@iO+ulHoCwX7B{&zc%1&Q-(aQu!Pa18O*_1qF%pahBYF~& z3apTPRjF;aOIyq=OC|421GiNEy%ZLk1C;-fK#Tto=|994F(6T*+Y%6eD?vTjjq-U3 zmLG9eO^o?R#Hu(_6oSzw{b&8D#Q6X;GV-K!+-bag#(zFA=s$-iVd7`;x!&K43%B6@AyID?+rD5!CtV>A(LOB)c<^ zG-R_0#65xPi|A}AcR3ApUg0jYmlXxhJoW>yGjdC}{++{4tsL$KJ0-`u`&zp!rlzY? zS6v}VNre=Z`;Ew-+j~bj2{=jyHXGGhjr2z@NZze{l&&1HmZcwFfA zHkbqE?IQh)0r3ywgQUa{^#lHr&TX9+{2i7}gl_zF>OKn$Kka;fl4T*eS9zBjUFLrhUqwz;e|G^L8Z6OFDxOvnX~Q?S`f80 zhXPhbfxUF%VB=rmPhdBx4L?})UWa~f!#E9ILD)CLbtq{_6di!f-PqD zF8{W`n|r&?beTx7RsA;DnwS58CuH{8cHr*kE1@q|I7hH`I--aDqx)RcV5xjFpdGqH z>XsM>48G|sn}B3*7ApCgH=Mdjiq*zdy1LP16jX2cEUJa*XR7pq$Kg?=0G3n z9g?POpG6X>A<7C8BT&PP2<+R5>BzxR9We5>z-lQd9dPQ$DdcTLNmPmE61s)}^3Wf7 z9!~o9!+CFGj>Gdg4sST8C+VROR5&2{c$OJP`-?`o!iI?C^#x{#K1xYC_K%fCaT;k| zO#FW|9Fjc{tISIoUFfqoMEy`Sk&aXl0~lpE!jNLZII;mqezMR~18h-FTj}VATV&~l zmwdm5zj*slCK$3yA!zmjn~tU;?6zJ!VYz%gpcyWEk7;J^qy`T-;V?r~E1X_{N@|#| z>2qq)4w@Y04T=A?!fLd_s|(byzsg>FsKho8|uMr*{|A!g=52LcVI7n43LayN&o0N zjL?@Bbg)6T*XVHWU9b}qKHKn#+Q3=s0NuHz%R)SI|=F@KrQJl3VDqFikU4e{$IgV75At)e;UpfGn^?^&>T^ z=P(2O*H&OHgeL6jFx>0qEuF||cFGi;j>v5%&_Y_7tAS)HLt!Rh$#O(;XmvI(=fw$c2HE^FzhG4zV%~X0{K`Fz~=~LHL`9`(@``b2lO&AJ@ zvg)x_&-=cQdrSEr;_he2sTax!ELWNzYd%DN+2$vH-|@C%+t}4F_+IxlkhOdX|1X%= zf4g%*SoX3Fis4lV(q?d#Y9Vh)5$WiVM+;rybJS4CVoItit9Bz;Sy?3& znO9O5P?qv>7W2pt=Z$XQ`osd+=r&?k#^m~d7Qe>YA}XmGJe?`3=0#QJpBe!y_*{!m z;50IYp4;FGwi;Q`U^QiV{9BltLqthkQ~I^WBK}^8s&9$ho3jI=78mRZwpO{Bp!zpv zFiPj84yFTQrL=Hf47Pp^tpR&uhFEE^wcH)k8>OcL9mWnSRzeF;VYgcB>cVOW1L_yp zSB<-C=f{JshTLlCuU4_&QvkN0%*t*hnt6>ko`=zEOn{Ok|%NG69gbd&Bp&w;PtIna5q`*WTHooBoANK+oUo9up*%v8U(tH-jD ze~6sc?JM4>MyBqCg5|;=Izzi(7GiqdsKI$hU&q#S(la_UUtVky&WXur7CoqK8#M@n zTH**QsZ7?>&WRLyRVpiqF4u_Y@?c$}+?56X3nFhh+{~1cn$A)UqvD;K^+b{& zIXU~UrV)I;H>+mulX&y!H($7)<kd_Ehcu$NI%_IV9SimN=BP4VsO zoEClDPN*UDkdYszOG$Koypd;0cVm` zdCz2()ce_LmtPubt#m1=8(DFWlKRiws-B1_ktnhOC3Sq(ff1CC9P^xdd(c8zE^^+V zRU?P-8)Yl@&&7*zL+9cqJgdeHmBs)!#1semo+ha%N8A8MFz!{Ke619KT8DSOVm0QA zhz>LL-LtpWjC*FoE*r_$=ey_g^|=S>2C@1Y$X?fGqLnJvghw4@(`rb5bC$1dF}|(u zhoZ`>B@WD3O|ulCSg>0qS{~skfr^jtVZkakqXr?_FuRV>s-~4}G>tunj#R8JA!@xa zA*F*S*<2kjn2nuWj~QlZ)`}F1XVnT+Fx#BVk1r_cMPvzL%~;Q;(<%=8$TVLYKDhz8 zD=8|A802?7ok#T}Dv10l9L3lbK=MjDc2Y^D7xGelkhY@?N~%XuPZ=JiegSnAYaIC9 z+iA+$Pd`v=Abn1lY$ECj|2@6Yv(df3YS7sv7kyB`+2k%9KekOksLCp@@yO27ajmT? zzS=Wnu8~7H?(lRtL*{C^8TY7hqpc|3M4EG_{$-jTR=sIG>H|BdIBomU&Kn7hZ9bv1 z%_j7=RKj4pnP6w)OXWl7a#>BLZa}%0);T}y;?L|9kXD5QK6DO%St-jaJ;~Us!unSy53b0S9L}`em`8)bO>MJ>_(j;I5s$q~5VKL}O!uP@vM8yJW@{06pF?_1kpu4<*Be+k~P`L#?LO zr<|$DY-&_VmjRf|0UW0uC0(^E_HaKjJN-*CgIo@J6LR^rRh;&RFr)x}j zcmMBk^gvECzHadzpN#4~dY4Jmd&;XOm + :param index: An integer, 1-based index of the PSU of which to query status + :return: Boolean, True if PSU is operating properly, False if PSU is faulty + """ + if index is None: + return False + + grep_key = "PSUL_Status" if index == 1 else "PSUR_Status" + grep_string = self.run_command(self.ipmi_sensor + ' | grep ' + grep_key) + status_byte = self.find_value(grep_string) + + if status_byte is None: + return False + + failure_detected = (int(status_byte, 16) >> 1) & 1 + input_lost = (int(status_byte, 16) >> 3) & 1 + if failure_detected or input_lost: + return False + else: + return True + + def get_psu_presence(self, index): + """ + Retrieves the presence status of power supply unit (PSU) defined + by 1-based index + :param index: An integer, 1-based index of the PSU of which to query status + :return: Boolean, True if PSU is plugged, False if not + """ + if index is None: + return False + + grep_key = "PSUL_Status" if index == 1 else "PSUR_Status" + grep_string = self.run_command(self.ipmi_sensor + ' | grep ' + grep_key) + status_byte = self.find_value(grep_string) + + if status_byte is None: + return False + + presence = (int(status_byte, 16) >> 0) & 1 + if presence: + return True + else: + return False diff --git a/device/celestica/x86_64-cel_seastone_2-r0/plugins/sfputil.py b/device/celestica/x86_64-cel_seastone_2-r0/plugins/sfputil.py new file mode 100755 index 000000000000..6f05527621fe --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/plugins/sfputil.py @@ -0,0 +1,179 @@ +# Platform-specific SFP transceiver interface for SONiC +# + +try: + import time + from sonic_sfp.sfputilbase import SfpUtilBase +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + + +class SfpUtil(SfpUtilBase): + """Platform-specific SfpUtil class""" + + PORT_START = 1 + PORT_END = 33 + QSFP_PORT_START = 1 + QSFP_PORT_END = 32 + + EEPROM_OFFSET = 1 + PORT_INFO_PATH = '/sys/class/seastone2_fpga' + + _port_name = "" + _port_to_eeprom_mapping = {} + _port_to_i2cbus_mapping = {} + + @property + def port_start(self): + return self.PORT_START + + @property + def port_end(self): + return self.PORT_END + + @property + def qsfp_ports(self): + return list(range(self.QSFP_PORT_START, self.QSFP_PORT_END + 1)) + + @property + def port_to_eeprom_mapping(self): + return self._port_to_eeprom_mapping + + @property + def port_to_i2cbus_mapping(self): + return self._port_to_i2cbus_mapping + + def get_port_name(self, port_num): + if port_num in self.qsfp_ports: + self._port_name = "QSFP" + str(port_num - self.QSFP_PORT_START + 1) + else: + self._port_name = "SFP" + str(port_num) + return self._port_name + + # def get_eeprom_dom_raw(self, port_num): + # if port_num in self.qsfp_ports: + # # QSFP DOM EEPROM is also at addr 0x50 and thus also stored in eeprom_ifraw + # return None + # else: + # # Read dom eeprom at addr 0x51 + # return self._read_eeprom_devid(port_num, self.DOM_EEPROM_ADDR, 256) + + def __init__(self): + # Override port_to_eeprom_mapping for class initialization + eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom' + + for x in range(self.PORT_START, self.PORT_END+1): + self.port_to_i2cbus_mapping[x] = (x + self.EEPROM_OFFSET) + self.port_to_eeprom_mapping[x] = eeprom_path.format( + x + self.EEPROM_OFFSET) + SfpUtilBase.__init__(self) + + def get_presence(self, port_num): + + # Check for invalid port_num + if port_num not in list(range(self.port_start, self.port_end + 1)): + return False + + # Get path for access port presence status + port_name = self.get_port_name(port_num) + sysfs_filename = "qsfp_modprs" if port_num in self.qsfp_ports else "sfp_modabs" + reg_path = "/".join([self.PORT_INFO_PATH, port_name, sysfs_filename]) + + # Read status + try: + reg_file = open(reg_path) + content = reg_file.readline().rstrip() + reg_value = int(content) + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + # Module present is active low + if reg_value == 0: + return True + + return False + + def get_low_power_mode(self, port_num): + # Check for invalid QSFP port_num + if port_num not in self.qsfp_ports: + return False + + try: + port_name = self.get_port_name(port_num) + reg_file = open( + "/".join([self.PORT_INFO_PATH, port_name, "qsfp_lpmode"]), "r+") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + # Read status + content = reg_file.readline().rstrip() + reg_value = int(content, 16) + # ModPrsL is active low + if reg_value == 0: + return False + + return True + + def set_low_power_mode(self, port_num, lpmode): + # Check for invalid QSFP port_num + if port_num not in self.qsfp_ports: + return False + + try: + port_name = self.get_port_name(port_num) + reg_file = open( + "/".join([self.PORT_INFO_PATH, port_name, "qsfp_lpmode"]), "r+") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + content = hex(lpmode) + + reg_file.seek(0) + reg_file.write(content) + reg_file.close() + + return True + + def reset(self, port_num): + # Check for invalid QSFP port_num + if port_num not in self.qsfp_ports: + return False + + try: + port_name = self.get_port_name(port_num) + reg_file = open( + "/".join([self.PORT_INFO_PATH, port_name, "qsfp_reset"]), "w") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + # Convert our register value back to a hex string and write back + reg_file.seek(0) + reg_file.write(hex(0)) + reg_file.close() + + # Sleep 1 second to allow it to settle + time.sleep(1) + + # Flip the bit back high and write back to the register to take port out of reset + try: + reg_file = open( + "/".join([self.PORT_INFO_PATH, port_name, "qsfp_reset"]), "w") + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + reg_file.seek(0) + reg_file.write(hex(1)) + reg_file.close() + + return True + + def get_transceiver_change_event(self, timeout=0): + """ + TBD: When the feature request. + """ + raise NotImplementedError diff --git a/device/celestica/x86_64-cel_seastone_2-r0/pmon_daemon_control.json b/device/celestica/x86_64-cel_seastone_2-r0/pmon_daemon_control.json new file mode 100644 index 000000000000..5e59513ef696 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/pmon_daemon_control.json @@ -0,0 +1,7 @@ +{ + "skip_ledd": true, + "skip_xcvrd": false, + "skip_psud": false, + "skip_syseepromd": false, + "skip_fancontrol": true +} \ No newline at end of file diff --git a/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/chassis.json b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/chassis.json new file mode 100644 index 000000000000..dfa1a1bddff8 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/chassis.json @@ -0,0 +1,45 @@ +{ + "eeprom": "/sys/class/i2c-adapter/i2c-0/0-0056/eeprom", + "get_reboot_cause": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x3a 0x0c 0x0 0x2 0x06", + "output_translator": { + "00": "Hardware - Other", + "11": "Hardware - Other", + "22": "Non-Hardware", + "33": "Hardware - Other", + "44": "Non-Hardware", + "55": "Non-Hardware", + "77": "Watchdog", + "88": "Thermal Overload: CPU", + "99": "Thermal Overload: ASIC" + } + }, + "get_reboot_description": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x3a 0x0c 0x0 0x2 0x06", + "output_translator": { + "00": "The last reset is power cycle reset (set register 0xA164)", + "11": "The last reset is Power on reset", + "22": "The last reset is soft-set CPU warm reset", + "33": "The last reset is soft-set CPU cold reset", + "44": "The last reset is CPU warm reset", + "55": "The last reset is CPU cold reset", + "77": "The last reset is watchdog reset", + "88": "The last reset is CPU thermal overload", + "99": "The last reset is ASIC thermal overload" + } + }, + "get_watchdog": { + "output_source": "class", + "host_path": "/usr/share/sonic/device/x86_64-cel_seastone_2-r0/sonic_platform_config/watchdog.py", + "pmon_path": "/usr/share/sonic/platform/sonic_platform_config/watchdog.py", + "class": "Watchdog" + }, + "get_change_event": { + "output_source": "class", + "host_path": "/usr/share/sonic/device/x86_64-cel_seastone_2-r0/sonic_platform_config/event.py", + "pmon_path": "/usr/share/sonic/platform/sonic_platform_config/event.py", + "class": "SfpEvent" + } +} diff --git a/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/component.json b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/component.json new file mode 100644 index 000000000000..96f646206f97 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/component.json @@ -0,0 +1,62 @@ +{ + "component_num": 5, + "get_name": { + "output_source": "value_list", + "value_list": [ + "BIOS", + "CPLD_BASEBOARD", + "CPLD_SWITCHBOARD", + "FPGA", + "BMC" + ] + }, + "get_description": { + "output_source": "value_list", + "value_list": [ + "Used to perform hardware initialization during the booting process", + "Used to control the system power & reset, Control FAN, UART Mux etc", + "Used for managing QSFP ports", + "Used for managing I2C, SPI, PCIe etc", + "Used for monitoring and managing whole system" + ] + }, + "get_firmware_version": { + "output_source": "function", + "function": [ + "_get_bios_ver", + "_get_base_cpld_ver", + "_get_sw_cpld_ver", + "_get_fpga_ver", + "_get_bmc_ver" + ] + }, + "_get_bmc_ver": { + "output_source": "ipmitool", + "command": "ipmitool mc info | grep 'Firmware Revision'", + "output_translator": "'{}'.split(':')[-1].strip()" + }, + "_get_bios_ver": { + "output_source": "txt_file", + "path": "/sys/class/dmi/id/bios_version" + }, + "_get_base_cpld_ver": { + "output_source": "hex_version_file", + "num_of_points": 1, + "num_of_bits": 8, + "path": "/sys/devices/platform/baseboard/version" + }, + "_get_sw_cpld_ver": { + "output_source": "hex_version_getreg", + "num_of_points": 1, + "num_of_bits": 8, + "reg_addr": "0x00", + "path": "/sys/devices/platform/switchboard/CPLD1/getreg" + }, + "_get_fpga_ver": { + "output_source": "hex_version_getreg", + "num_of_points": 1, + "num_of_bits": 32, + "reg_addr": "0x00", + "path": "/sys/devices/platform/switchboard/FPGA/getreg" + } +} \ No newline at end of file diff --git a/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/event.py b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/event.py new file mode 100644 index 000000000000..ed3b78cf7fd6 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/event.py @@ -0,0 +1,112 @@ +############################################################################# +# Celestica Seastone2 +# +# SfpEvent contains an implementation of SONiC Platform Base API +# +############################################################################# +try: + import time + import os + from sonic_platform.common import Common +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +PLATFORM_PATH = "/sys/devices/platform/" +SWITCH_BRD_PLATFORM = "switchboard" +POLL_INTERVAL = 1 + + +class SfpEvent: + ''' Listen to insert/remove sfp events ''' + + PORT_INFO_DIR = 'SFF' + PATH_INT_SYSFS = "{0}/{port_name}/{type_prefix}_isr_flags" + PATH_INTMASK_SYSFS = "{0}/{port_name}/{type_prefix}_isr_mask" + PATH_PRS_SYSFS = "{0}/{port_name}/{prs_file_name}" + PRESENT_EN = 0x01 + + def __init__(self, sfp_list): + self.num_sfp = len(sfp_list) + self._api_common = Common() + self._initialize_interrupts() + + def _initialize_interrupts(self): + sfp_info_obj = {} + port_info_path = os.path.join( + PLATFORM_PATH, SWITCH_BRD_PLATFORM, self.PORT_INFO_DIR) + + for index in range(self.num_sfp): + port_num = index + 1 + port_name = "QSFP{}".format(port_num) + port_type = "qsfp" + sysfs_prs_file = "{}_modprs".format(port_type) + + sfp_info_obj[index] = {} + sfp_info_obj[index]['intmask_sysfs'] = self.PATH_INTMASK_SYSFS.format( + port_info_path, + port_name=port_name, + type_prefix=port_type) + + sfp_info_obj[index]['int_sysfs'] = self.PATH_INT_SYSFS.format( + port_info_path, + port_name=port_name, + type_prefix=port_type) + + sfp_info_obj[index]['prs_sysfs'] = self.PATH_PRS_SYSFS.format( + port_info_path, + port_name=port_name, + prs_file_name=sysfs_prs_file) + + self._api_common.write_txt_file( + sfp_info_obj[index]["intmask_sysfs"], hex(self.PRESENT_EN)) + + self.sfp_info_obj = sfp_info_obj + + def _is_port_device_present(self, port_idx): + prs_path = self.sfp_info_obj[port_idx]["prs_sysfs"] + is_present = 1 - int(self._api_common.read_txt_file(prs_path)) + return is_present + + def _update_port_event_object(self, interrup_devices, port_dict): + for port_idx in interrup_devices: + device_id = str(port_idx + 1) + port_dict[device_id] = str(self._is_port_device_present(port_idx)) + return port_dict + + def _clear_event_flag(self, path): + self._api_common.write_txt_file(path, hex(0xff)) + time.sleep(0.1) + self._api_common.write_txt_file(path, hex(0x0)) + + def _check_all_port_interrupt_event(self): + interrupt_devices = {} + for i in range(self.num_sfp): + int_sysfs = self.sfp_info_obj[i]["int_sysfs"] + interrupt_flags = self._api_common.read_txt_file(int_sysfs) + if interrupt_flags != '0x00': + interrupt_devices[i] = 1 + self._clear_event_flag(int_sysfs) + return interrupt_devices + + def get_event(self, timeout): + sleep_time = min( + timeout, POLL_INTERVAL) if timeout != 0 else POLL_INTERVAL + start_milli_time = int(round(time.time() * 1000)) + int_sfp = {} + + while True: + chk_sfp = self._check_all_port_interrupt_event() + int_sfp = self._update_port_event_object( + chk_sfp, int_sfp) if chk_sfp else int_sfp + current_milli_time = int(round(time.time() * 1000)) + if (int_sfp) or \ + (timeout != 0 and current_milli_time - start_milli_time > timeout): + break + + time.sleep(sleep_time) + + change_dict = dict() + change_dict['sfp'] = int_sfp + + return True, change_dict diff --git a/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/fan.json b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/fan.json new file mode 100644 index 000000000000..724f6edf53c1 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/fan.json @@ -0,0 +1,200 @@ +{ + "fan_num_per_drawer": 2, + "drawer_num": 4, + "get_name": { + "output_source": "value_list", + "value_list": [ + "Fan1-F", + "Fan1-R", + "Fan2-F", + "Fan2-R", + "Fan3-F", + "Fan3-R", + "Fan4-F", + "Fan4-R" + ] + }, + "get_presence": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x3a 0x03 0x03 {}", + "argument": [ + "0x00", + "0x00", + "0x01", + "0x01", + "0x02", + "0x02", + "0x03", + "0x03" + ], + "output_translator": "True if '00' in '{}' else False" + }, + "get_model": { + "output_source": "ipmitool", + "command": "ipmitool fru list {} | grep 'Board Part Number'", + "argument": [ + "5", + "5", + "6", + "6", + "7", + "7", + "8", + "8" + ], + "output_translator": "'{}'.split()[-1]" + }, + "get_serial": { + "output_source": "ipmitool", + "command": "ipmitool fru list {} | grep 'Board Serial'", + "argument": [ + "5", + "5", + "6", + "6", + "7", + "7", + "8", + "8" + ], + "output_translator": "'{}'.split()[-1]" + }, + "get_direction": { + "output_source": "ipmitool", + "command": "ipmitool fru list {} | grep 'F2B\\|B2F'", + "argument": [ + "5", + "5", + "6", + "6", + "7", + "7", + "8", + "8" + ], + "output_translator": "'intake' if 'B2F' in '{}' else 'exhaust'" + }, + "get_speed": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x04 0x2d {}", + "argument": [ + "0x81", + "0x80", + "0x83", + "0x82", + "0x85", + "0x84", + "0x87", + "0x86" + ], + "output_translator": "int('{}'.split()[0],16)*150", + "max_front": 23000, + "max_rear": 20500 + }, + "get_target_speed": { + "output_source": "value", + "value": "N/A" + }, + "get_speed_tolerance": { + "output_source": "value", + "value": 10 + }, + "set_speed": { + "set_method": "ipmitool", + "input_translator": "hex(int({} * 255 / 100.0))", + "command": "ipmitool raw 0x3a 0x0c 0x00 0x03 {}", + "argument": [ + "0x40 {}", + "0x40 {}", + "0x44 {}", + "0x44 {}", + "0x4c {}", + "0x4c {}", + "0x50 {}", + "0x50 {}" + ] + }, + "set_status_led": { + "set_method": "ipmitool", + "avaliable_input": [ + "off", + "amber", + "green" + ], + "input_translator": { + "off": "0x0", + "amber": "0x1", + "green": "0x2" + }, + "command": "ipmitool raw 0x3a 0x0a {}", + "argument": [ + "0x4 {}", + "0x4 {}", + "0x5 {}", + "0x5 {}", + "0x6 {}", + "0x6 {}", + "0x7 {}", + "0x7 {}" + ] + }, + "get_status_led": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x3a 0x0b {}", + "argument": [ + "0x4", + "0x4", + "0x5", + "0x5", + "0x6", + "0x6", + "0x7", + "0x7" + ], + "output_translator": { + "00": "off", + "01": "amber", + "02": "green" + } + }, + "psu_fan": [ + { + "num_of_fan": 1, + "get_name": { + "output_source": "value_list", + "value_list": [ + "PSU-R-Fan" + ] + }, + "get_speed": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x04 0x2d {}", + "argument": [ + "0x8b" + ], + "output_translator": "int('{}'.split()[0],16)*100", + "max_front": 22600, + "max_rear": 22600 + } + }, + { + "num_of_fan": 1, + "get_name": { + "output_source": "value_list", + "value_list": [ + "PSU-L-Fan" + ] + }, + "get_speed": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x04 0x2d {}", + "argument": [ + "0x8a" + ], + "output_translator": "int('{}'.split()[0],16)*100", + "max_front": 22600, + "max_rear": 22600 + } + } + ] +} \ No newline at end of file diff --git a/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/psu.json b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/psu.json new file mode 100644 index 000000000000..c3e953c082a7 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/psu.json @@ -0,0 +1,135 @@ +{ + "psu_num": 2, + "fan_per_psu_num": 1, + "get_name": { + "output_source": "value_list", + "value_list": [ + "PSU-R", + "PSU-L" + ] + }, + "get_power": { + "output_source": "ipmitool", + "command": "ipmitool sdr | grep {}", + "argument": [ + "PSUR_POut", + "PSUL_POut" + ], + "output_translator": "float('{}'.split()[2])" + }, + "get_current": { + "output_source": "ipmitool", + "command": "ipmitool sdr | grep {}", + "argument": [ + "PSUR_COut", + "PSUL_COut" + ], + "output_translator": "float('{}'.split()[2])" + }, + "get_voltage": { + "output_source": "ipmitool", + "command": "ipmitool sdr | grep {}", + "argument": [ + "PSUR_VOut", + "PSUL_VOut" + ], + "output_translator": "float('{}'.split()[2])" + }, + "get_voltage_high_threshold": { + "output_source": "ipmitool", + "command": "ipmitool sensor list | grep {}", + "argument": [ + "PSUR_Temp2", + "PSUL_Temp2" + ], + "output_translator": "float(0 if '{0}'.split()[-3]=='na' else '{0}'.split()[-3])" + }, + "get_voltage_low_threshold": { + "output_source": "ipmitool", + "command": "ipmitool sensor list | grep {}", + "argument": [ + "PSUR_Temp2", + "PSUL_Temp2" + ], + "output_translator": "float(0 if '{0}'.split()[-9]=='na' else '{0}'.split()[-9])" + }, + "get_presence": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x3a 0x0c 0x00 0x2 0x60", + "output_translator": [ + "True if (int('{}', 16) >> 4 & 1) == 0 else False", + "True if (int('{}', 16) >> 5 & 1) == 0 else False" + ] + }, + "get_model": { + "output_source": "ipmitool", + "command": "ipmitool fru list {} | grep 'Product Part Number'", + "argument": [ + "4", + "3" + ], + "output_translator": "'{}'.split()[-1]" + }, + "get_serial": { + "output_source": "ipmitool", + "command": "ipmitool fru list {} | grep 'Product Serial'", + "argument": [ + "4", + "3" + ], + "output_translator": "'{}'.split()[-1]" + }, + "get_powergood_status": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x3a 0x0c 0x0 0x2 0x60", + "output_translator": [ + "True if (int('{}', 16) >> 2 & 1) == 1 else False", + "True if (int('{}', 16) >> 3 & 1) == 1 else False" + ] + }, + "set_status_led": { + "set_method": "ipmitool", + "avaliable_input": [ + "amber" + ], + "input_translator": { + "amber": "0x1" + }, + "command": "ipmitool raw 0x3a 0x0a {}", + "argument": [ + "0x3 {}", + "0x2 {}" + ] + }, + "get_status_led": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x3a 0x0b {}", + "argument": [ + "0x3", + "0x2" + ], + "output_translator": { + "00": "green", + "01": "amber" + }, + "default_output": "off" + }, + "get_temperature": { + "output_source": "ipmitool", + "command": "ipmitool sdr | grep {}", + "argument": [ + "PSUR_Temp2", + "PSUL_Temp2" + ], + "output_translator": "float('{}'.split()[2])" + }, + "get_temperature_high_threshold": { + "output_source": "ipmitool", + "command": "ipmitool sensor list | grep {}", + "argument": [ + "PSUR_Temp2", + "PSUL_Temp2" + ], + "output_translator": "float(0 if '{0}'.split()[-3]=='na' else '{0}'.split()[-3])" + } +} \ No newline at end of file diff --git a/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/sfp.json b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/sfp.json new file mode 100644 index 000000000000..d77e75e50e2f --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/sfp.json @@ -0,0 +1,106 @@ +{ + "port_num": 32, + "eeprom_path": "/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom", + "port_i2c_mapping": [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33 + ], + "get_presence": { + "output_source": "sysfs_value", + "sysfs_path": "/sys/devices/platform/switchboard/SFF/{}/qsfp_modprs", + "argument": "$ref:_port_name", + "output_translator": "False if '{}' == '1' else True" + }, + "get_lpmode": { + "output_source": "sysfs_value", + "sysfs_path": "/sys/devices/platform/switchboard/SFF/{}/qsfp_lpmode", + "argument": "$ref:_port_name", + "output_translator": "True if '{}' == '1' else False" + }, + "get_reset_status": { + "output_source": "sysfs_value", + "sysfs_path": "/sys/devices/platform/switchboard/SFF/{}/qsfp_reset", + "argument": "$ref:_port_name", + "output_translator": "False if '{}' == '1' else True" + }, + "reset": { + "set_method": "sysfs_value", + "write_offset": 0, + "sysfs_path": "/sys/devices/platform/switchboard/SFF/{}/qsfp_reset", + "argument": "$ref:_port_name" + }, + "set_lpmode": { + "set_method": "sysfs_value", + "input_translator": { + "True": "0x1", + "False": "0x0" + }, + "write_offset": 0, + "sysfs_path": "/sys/devices/platform/switchboard/SFF/{}/qsfp_lpmode", + "argument": "$ref:_port_name" + }, + "_port_name": [ + "QSFP1", + "QSFP2", + "QSFP3", + "QSFP4", + "QSFP5", + "QSFP6", + "QSFP7", + "QSFP8", + "QSFP9", + "QSFP10", + "QSFP11", + "QSFP12", + "QSFP13", + "QSFP14", + "QSFP15", + "QSFP16", + "QSFP17", + "QSFP18", + "QSFP19", + "QSFP20", + "QSFP21", + "QSFP22", + "QSFP23", + "QSFP24", + "QSFP25", + "QSFP26", + "QSFP27", + "QSFP28", + "QSFP29", + "QSFP30", + "QSFP31", + "QSFP32" + ] +} \ No newline at end of file diff --git a/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/thermal.json b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/thermal.json new file mode 100644 index 000000000000..e3dcb7fb9271 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/thermal.json @@ -0,0 +1,105 @@ +{ + "thermal_num": 9, + "get_name": { + "output_source": "value_list", + "value_list": [ + "Base_Temp_U5", + "Base_Temp_U7", + "CPU_Temp", + "Switch_Temp_U1", + "Switch_Temp_U18", + "Switch_Temp_U28", + "Switch_Temp_U29", + "Switch_U21_Temp", + "Switch_U33_Temp" + ] + }, + "get_temperature": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x04 0x2D {}", + "argument": [ + "0x1", + "0x2", + "0x7", + "0x3", + "0x4", + "0x5", + "0x6", + "0x56", + "0x4C" + ], + "output_translator": "int('{}'.split()[0],16)" + }, + "get_high_threshold": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x04 0x27 {}", + "argument": [ + "0x1", + "0x2", + "0x7", + "0x3", + "0x4", + "0x5", + "0x6", + "0x56", + "0x4c" + ], + "output_translator": "int('{}'.split()[4], 16)" + }, + "get_low_threshold": { + "output_source": "value", + "value": "N/A" + }, + "set_high_threshold": { + "set_method": "ipmitool", + "command": "ipmitool sensor thresh {}", + "input_translator": "{}", + "argument": [ + "Base_Temp_U5 unc {}", + "Base_Temp_U7 unc {}", + "CPU_Temp unc {}", + "Switch_Temp_U1 unc {}", + "Switch_Temp_U18 unc {}", + "Switch_Temp_U28 unc {}", + "Switch_Temp_U29 unc {}", + "Switch_U21_Temp unc {}", + "Switch_U33_Temp unc {}" + ] + }, + "set_low_threshold": { + "output_source": "ipmitool", + "command": "ipmitool sensor thresh {}", + "input_translator": "{}", + "argument": [ + "Base_Temp_U5 lnc {}", + "Base_Temp_U7 lnc {}", + "CPU_Temp lnc {}", + "Switch_Temp_U1 lnc {}", + "Switch_Temp_U18 lnc {}", + "Switch_Temp_U28 lnc {}", + "Switch_Temp_U29 lnc {}", + "Switch_U21_Temp lnc {}", + "Switch_U33_Temp lnc {}" + ] + }, + "get_high_critical_threshold": { + "output_source": "ipmitool", + "command": "ipmitool raw 0x04 0x27 {}", + "argument": [ + "0x1", + "0x2", + "0x7", + "0x3", + "0x4", + "0x5", + "0x6", + "0x56", + "0x4c" + ], + "output_translator": "int('{}'.split()[5], 16)" + }, + "get_low_critical_threshold": { + "output_source": "value", + "value": "N/A" + } +} \ No newline at end of file diff --git a/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/watchdog.py b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/watchdog.py new file mode 100644 index 000000000000..fac5c40e134f --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/sonic_platform_config/watchdog.py @@ -0,0 +1,191 @@ +############################################################################# +# Celestica Seastone2 +# +# Watchdog contains an implementation of SONiC Platform Base API +# +############################################################################# +import os +import time + +try: + from sonic_platform_base.watchdog_base import WatchdogBase + from sonic_platform.common import Common +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +PLATFORM_CPLD_PATH = '/sys/devices/platform/baseboard/' +GETREG_FILE = 'getreg' +SETREG_FILE = 'setreg' +WDT_ENABLE_REG = '0xA181' +WDT_TIMER_L_BIT_REG = '0xA182' +WDT_TIMER_M_BIT_REG = '0xA183' +WDT_TIMER_H_BIT_REG = '0xA184' +WDT_KEEP_ALVIVE_REG = '0xA185' +ENABLE_CMD = '0x1' +DISABLE_CMD = '0x0' +WDT_COMMON_ERROR = -1 + + +class Watchdog(WatchdogBase): + + def __init__(self): + WatchdogBase.__init__(self) + + self._api_common = Common() + + # Init cpld reg path + self.setreg_path = os.path.join(PLATFORM_CPLD_PATH, SETREG_FILE) + self.getreg_path = os.path.join(PLATFORM_CPLD_PATH, GETREG_FILE) + + # Set default value + self._disable() + self.armed = False + self.timeout = self._gettimeout() + + def _enable(self): + """ + Turn on the watchdog timer + """ + # echo 0xA181 0x1 > /sys/devices/platform/baseboard/setreg + enable_val = '{} {}'.format(WDT_ENABLE_REG, ENABLE_CMD) + return self._api_common.write_txt_file(self.setreg_path, enable_val) + + def _disable(self): + """ + Turn off the watchdog timer + """ + # echo 0xA181 0x0 > /sys/devices/platform/baseboard/setreg + disable_val = '{} {}'.format(WDT_ENABLE_REG, DISABLE_CMD) + return self._api_common.write_txt_file(self.setreg_path, disable_val) + + def _keepalive(self): + """ + Keep alive watchdog timer + """ + # echo 0xA185 0x1 > /sys/devices/platform/baseboard/setreg + enable_val = '{} {}'.format(WDT_KEEP_ALVIVE_REG, ENABLE_CMD) + return self._api_common.write_txt_file(self.setreg_path, enable_val) + + def _get_level_hex(self, sub_hex): + sub_hex_str = sub_hex.replace("x", "0") + return hex(int(sub_hex_str, 16)) + + def _seconds_to_lmh_hex(self, seconds): + ms = seconds*1000 # calculate timeout in ms format + hex_str = hex(ms) + l = self._get_level_hex(hex_str[-2:]) + m = self._get_level_hex(hex_str[-4:-2]) + h = self._get_level_hex(hex_str[-6:-4]) + return (l, m, h) + + def _settimeout(self, seconds): + """ + Set watchdog timer timeout + @param seconds - timeout in seconds + @return is the actual set timeout + """ + # max = 0xffffff = 16777.215 seconds + + (l, m, h) = self._seconds_to_lmh_hex(seconds) + set_h_val = '{} {}'.format(WDT_TIMER_H_BIT_REG, h) + set_m_val = '{} {}'.format(WDT_TIMER_M_BIT_REG, m) + set_l_val = '{} {}'.format(WDT_TIMER_L_BIT_REG, l) + + self._api_common.write_txt_file( + self.setreg_path, set_h_val) # set high bit + self._api_common.write_txt_file( + self.setreg_path, set_m_val) # set med bit + self._api_common.write_txt_file( + self.setreg_path, set_l_val) # set low bit + + return seconds + + def _gettimeout(self): + """ + Get watchdog timeout + @return watchdog timeout + """ + + h_bit = self._api_common.get_reg( + self.getreg_path, WDT_TIMER_H_BIT_REG) + m_bit = self._api_common.get_reg( + self.getreg_path, WDT_TIMER_M_BIT_REG) + l_bit = self._api_common.get_reg( + self.getreg_path, WDT_TIMER_L_BIT_REG) + + hex_time = '0x{}{}{}'.format(h_bit[2:], m_bit[2:], l_bit[2:]) + ms = int(hex_time, 16) + return int(float(ms)/1000) + + ################################################################# + + def arm(self, seconds): + """ + Arm the hardware watchdog with a timeout of seconds. + If the watchdog is currently armed, calling this function will + simply reset the timer to the provided value. If the underlying + hardware does not support the value provided in , this + method should arm the watchdog with the *next greater* available + value. + Returns: + An integer specifying the *actual* number of seconds the watchdog + was armed with. On failure returns -1. + """ + + ret = WDT_COMMON_ERROR + if seconds < 0: + return ret + + try: + if self.timeout != seconds: + self.timeout = self._settimeout(seconds) + + if self.armed: + self._keepalive() + else: + self._enable() + self.armed = True + + ret = self.timeout + self.arm_timestamp = time.time() + except IOError as e: + pass + + return ret + + def disarm(self): + """ + Disarm the hardware watchdog + Returns: + A boolean, True if watchdog is disarmed successfully, False if not + """ + disarmed = False + if self.is_armed(): + try: + self._disable() + self.armed = False + disarmed = True + except IOError: + pass + + return disarmed + + def is_armed(self): + """ + Retrieves the armed state of the hardware watchdog. + Returns: + A boolean, True if watchdog is armed, False if not + """ + + return self.armed + + def get_remaining_time(self): + """ + If the watchdog is armed, retrieve the number of seconds remaining on + the watchdog timer + Returns: + An integer specifying the number of seconds remaining on thei + watchdog timer. If the watchdog is not armed, returns -1. + """ + + return int(self.timeout - (time.time() - self.arm_timestamp)) if self.armed else WDT_COMMON_ERROR From 70a0e26388bfa710b97974c54d5336774a3aeb85 Mon Sep 17 00:00:00 2001 From: Kantaphon Kovathana Date: Thu, 18 Mar 2021 14:47:33 +0700 Subject: [PATCH 2/4] [platform/cel] add seatone2 platform --- platform/broadcom/one-image.mk | 3 +- platform/broadcom/platform-modules-cel.mk | 12 +- .../sonic-platform-modules-cel/README.md | 2 +- .../debian/changelog | 6 + .../sonic-platform-modules-cel/debian/control | 7 +- .../debian/platform-modules-seastone2.init | 52 + .../debian/platform-modules-seastone2.install | 4 + .../platform-modules-seastone2.postinst | 5 + .../sonic-platform-modules-cel/debian/rules | 2 +- .../seastone2/cfg/seastone2-modules.conf | 16 + .../seastone2/modules/Makefile | 2 + .../seastone2/modules/baseboard_cpld.c | 378 +++ .../seastone2/modules/mc24lc64t.c | 174 ++ .../seastone2/modules/switchboard_fpga.c | 2365 +++++++++++++++++ .../platform-modules-seastone2.service | 14 + 15 files changed, 3034 insertions(+), 8 deletions(-) create mode 100644 platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.init create mode 100644 platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.install create mode 100644 platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.postinst create mode 100644 platform/broadcom/sonic-platform-modules-cel/seastone2/cfg/seastone2-modules.conf create mode 100644 platform/broadcom/sonic-platform-modules-cel/seastone2/modules/Makefile create mode 100644 platform/broadcom/sonic-platform-modules-cel/seastone2/modules/baseboard_cpld.c create mode 100644 platform/broadcom/sonic-platform-modules-cel/seastone2/modules/mc24lc64t.c create mode 100644 platform/broadcom/sonic-platform-modules-cel/seastone2/modules/switchboard_fpga.c create mode 100644 platform/broadcom/sonic-platform-modules-cel/seastone2/systemd/platform-modules-seastone2.service diff --git a/platform/broadcom/one-image.mk b/platform/broadcom/one-image.mk index 24c320d69ddc..2b53d794067a 100755 --- a/platform/broadcom/one-image.mk +++ b/platform/broadcom/one-image.mk @@ -34,7 +34,8 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \ $(QUANTA_IX1B_32X_PLATFORM_MODULE) \ $(MITAC_LY1200_32X_PLATFORM_MODULE) \ $(ALPHANETWORKS_SNH60A0_320FV2_PLATFORM_MODULE) \ - $(ALPHANETWORKS_SNH60B0_640F_PLATFORM_MODULE) + $(ALPHANETWORKS_SNH60B0_640F_PLATFORM_MODULE) \ + $(CEL_SEASTONE2_PLATFORM_MODULE) ifeq ($(INSTALL_DEBUG_TOOLS),y) $(SONIC_ONE_IMAGE)_DOCKERS = $(SONIC_INSTALL_DOCKER_DBG_IMAGES) $(SONIC_ONE_IMAGE)_DOCKERS += $(filter-out $(patsubst %-$(DBG_IMAGE_MARK).gz,%.gz, $(SONIC_INSTALL_DOCKER_DBG_IMAGES)), $(SONIC_INSTALL_DOCKER_IMAGES)) diff --git a/platform/broadcom/platform-modules-cel.mk b/platform/broadcom/platform-modules-cel.mk index 1224faaa6750..2ead4c6f50e6 100644 --- a/platform/broadcom/platform-modules-cel.mk +++ b/platform/broadcom/platform-modules-cel.mk @@ -1,10 +1,12 @@ -# Celestica DX010 and Haliburton Platform modules +# Celestica platform modules -CEL_DX010_PLATFORM_MODULE_VERSION = 0.9 -CEL_HALIBURTON_PLATFORM_MODULE_VERSION = 0.9 +CEL_DX010_PLATFORM_MODULE_VERSION = 1.0 +CEL_HALIBURTON_PLATFORM_MODULE_VERSION = 1.0 +CEL_SEASTONE2_PLATFORM_MODULE_VERSION = 1.0 export CEL_DX010_PLATFORM_MODULE_VERSION export CEL_HALIBURTON_PLATFORM_MODULE_VERSION +export CEL_SEASTONE2_PLATFORM_MODULE_VERSION CEL_DX010_PLATFORM_MODULE = platform-modules-dx010_$(CEL_DX010_PLATFORM_MODULE_VERSION)_amd64.deb $(CEL_DX010_PLATFORM_MODULE)_SRC_PATH = $(PLATFORM_PATH)/sonic-platform-modules-cel @@ -16,4 +18,8 @@ CEL_HALIBURTON_PLATFORM_MODULE = platform-modules-haliburton_$(CEL_HALIBURTON_PL $(CEL_HALIBURTON_PLATFORM_MODULE)_PLATFORM = x86_64-cel_e1031-r0 $(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_HALIBURTON_PLATFORM_MODULE))) +CEL_SEASTONE2_PLATFORM_MODULE = platform-modules-seastone2_$(CEL_SEASTONE2_PLATFORM_MODULE_VERSION)_amd64.deb +$(CEL_SEASTONE2_PLATFORM_MODULE)_PLATFORM = x86_64-cel_seastone_2-r0 +$(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_SEASTONE2_PLATFORM_MODULE))) + SONIC_STRETCH_DEBS += $(CEL_DX010_PLATFORM_MODULE) diff --git a/platform/broadcom/sonic-platform-modules-cel/README.md b/platform/broadcom/sonic-platform-modules-cel/README.md index 26e45c685e44..9881d6ecb1d0 100644 --- a/platform/broadcom/sonic-platform-modules-cel/README.md +++ b/platform/broadcom/sonic-platform-modules-cel/README.md @@ -1 +1 @@ -platform drivers for Celestica DX010 and Haliburton for the SONiC project +platform drivers of Celestica network switchs for the SONiC project diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/changelog b/platform/broadcom/sonic-platform-modules-cel/debian/changelog index 843b028e4301..dc577ee2025c 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/changelog +++ b/platform/broadcom/sonic-platform-modules-cel/debian/changelog @@ -1,3 +1,9 @@ +sonic-cel-platform-modules (1.0) unstable; urgency=low + + * Introduce seastone2 platform module. + + -- Kantaphon Kovathana Wed, 17 March 2021 15:53:30 +0700 + sonic-cel-platform-modules (0.9) unstable; urgency=low * Add haliburton platform module. diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/control b/platform/broadcom/sonic-platform-modules-cel/debian/control index 1cce3fea8cfc..bb99d15ae53b 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/control +++ b/platform/broadcom/sonic-platform-modules-cel/debian/control @@ -1,7 +1,7 @@ Source: sonic-cel-platform-modules Section: main Priority: extra -Maintainer: Abhisit Sangjan +Maintainer: Wirut Getbamrung Build-Depends: debhelper (>= 8.0.0), bzip2 Standards-Version: 3.9.3 @@ -10,8 +10,11 @@ Architecture: amd64 Depends: linux-image-4.9.0-14-amd64 Description: kernel modules for platform devices such as fan, led, sfp - Package: platform-modules-haliburton Architecture: amd64 Depends: linux-image-4.9.0-14-amd64 Description: kernel modules for platform devices such as fan, led, sfp + +Package: platform-modules-seastone2 +Architecture: amd64 +Description: kernel modules for platform devices such as fan, led, sfp \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.init b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.init new file mode 100644 index 000000000000..977cdac06000 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.init @@ -0,0 +1,52 @@ +#!/bin/bash + +### BEGIN INIT INFO +# Provides: setup-board +# Required-Start: $portmap +# Required-Stop: +# Should-Start: +# Should-Stop: +# Default-Start: S +# Default-Stop: 0 6 +# Short-Description: Setup seastone2 board. +### END INIT INFO + +case "$1" in +start) + echo -n "Setting up board... " + + # Add driver to support HW + modprobe i2c-dev + modprobe ipmi_devintf + modprobe ipmi_si + modprobe baseboard_cpld + modprobe switchboard_fpga + modprobe mc24lc64t + + # Add driver to support TLV - EEPROM + for devnum in 0 1; do + devname=`cat /sys/bus/i2c/devices/i2c-${devnum}/name` + if [[ $devname == 'SMBus iSMT adapter at '* ]]; then + echo 24lc64t 0x56 > /sys/bus/i2c/devices/i2c-${devnum}/new_device + break + fi + done + sleep 1 + echo "done." + ;; + +stop) + echo "done." + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/platform-modules-seastone2.init {start|stop}" + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.install b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.install new file mode 100644 index 000000000000..41a381eeb0ce --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.install @@ -0,0 +1,4 @@ +seastone2/cfg/seastone2-modules.conf etc/modules-load.d +seastone2/systemd/platform-modules-seastone2.service lib/systemd/system +seastone2/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/x86_64-cel_seastone_2-r0 +services/platform_api/platform_api_mgnt.sh usr/local/bin \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.postinst b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.postinst new file mode 100644 index 000000000000..f232a2cac59d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.postinst @@ -0,0 +1,5 @@ +depmod -a +systemctl enable platform-modules-seastone2.service +systemctl start platform-modules-seastone2.service + +/usr/local/bin/platform_api_mgnt.sh install diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/rules b/platform/broadcom/sonic-platform-modules-cel/debian/rules index 6f35290bce26..44ac6c9d3650 100755 --- a/platform/broadcom/sonic-platform-modules-cel/debian/rules +++ b/platform/broadcom/sonic-platform-modules-cel/debian/rules @@ -5,7 +5,7 @@ export INSTALL_MOD_DIR:=extra KVERSION ?= $(shell uname -r) KERNEL_SRC := /lib/modules/$(KVERSION) MOD_SRC_DIR:= $(shell pwd) -MODULE_DIRS:= dx010 haliburton +MODULE_DIRS:= dx010 haliburton seastone2 %: dh $@ diff --git a/platform/broadcom/sonic-platform-modules-cel/seastone2/cfg/seastone2-modules.conf b/platform/broadcom/sonic-platform-modules-cel/seastone2/cfg/seastone2-modules.conf new file mode 100644 index 000000000000..795e6af74305 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/seastone2/cfg/seastone2-modules.conf @@ -0,0 +1,16 @@ +# /etc/modules: kernel modules to load at boot time. +# +# This file contains the names of kernel modules that should be loaded +# at boot time, one per line. Lines beginning with "#" are ignored. + +i2c-i801 +i2c-isch +i2c-ismt +i2c-dev +i2c-mux +i2c-smbus + +i2c-mux-gpio +i2c-mux-pca954x +ipmi_devintf +ipmi_si diff --git a/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/Makefile b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/Makefile new file mode 100644 index 000000000000..3b59a78682a0 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/Makefile @@ -0,0 +1,2 @@ +KBUILD_CFLAGS+=-DSEASTONE2 +obj-m := mc24lc64t.o baseboard_cpld.o switchboard_fpga.o \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/baseboard_cpld.c b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/baseboard_cpld.c new file mode 100644 index 000000000000..07ab42c21686 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/baseboard_cpld.c @@ -0,0 +1,378 @@ +/* + * baseboard_cpld.c - driver for Seastone2 Base Board CPLD + * This driver implement sysfs for CPLD register access using LPC bus. + * Copyright (C) 2019 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "baseboard" +/** + * CPLD register address for read and write. + */ +#define VERSION_ADDR 0xA100 +#define SCRATCH_ADDR 0xA101 +#define SYS_LED_ADDR 0xA162 + +#define CPLD_REGISTER_SIZE 0x77 + +struct baseboard_cpld_data { + struct mutex cpld_lock; + uint16_t read_addr; +}; + +struct baseboard_cpld_data *cpld_data; + +/** + * Read the value from scratch register as hex string. + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return Hex string read from scratch register. + */ +static ssize_t scratch_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + mutex_lock(&cpld_data->cpld_lock); + data = inb(SCRATCH_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return sprintf(buf,"0x%2.2x\n", data); +} + +/** + * Set scratch register with specific hex string. + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t scratch_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned long data; + char *last; + + mutex_lock(&cpld_data->cpld_lock); + data = (uint16_t)strtoul(buf,&last,16); + if(data == 0 && buf == last){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + outb(data, SCRATCH_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_RW(scratch); + + +/* CPLD version attributes */ +static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int len = -EIO; + // CPLD register is one byte + mutex_lock(&cpld_data->cpld_lock); + len = sprintf(buf, "0x%2.2x\n",inb(VERSION_ADDR)); + mutex_unlock(&cpld_data->cpld_lock); + return len; +} +static DEVICE_ATTR_RO(version); + + +static ssize_t getreg_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + // CPLD register is one byte + uint16_t addr; + char *last; + + addr = (uint16_t)strtoul(buf,&last,16); + if(addr == 0 && buf == last){ + return -EINVAL; + } + cpld_data->read_addr = addr; + return count; +} + +static ssize_t getreg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int len = -EIO; + // CPLD register is one byte + mutex_lock(&cpld_data->cpld_lock); + len = sprintf(buf, "0x%2.2x\n",inb(cpld_data->read_addr)); + mutex_unlock(&cpld_data->cpld_lock); + return len; +} +static DEVICE_ATTR_RW(getreg); + +static ssize_t setreg_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + // CPLD register is one byte + uint16_t addr; + uint8_t value; + char *tok; + char clone[count]; + char *pclone = clone; + char *last; + + strcpy(clone, buf); + + mutex_lock(&cpld_data->cpld_lock); + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + addr = (uint16_t)strtoul(tok,&last,16); + if(addr == 0 && tok == last){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + value = (uint8_t)strtoul(tok,&last,16); + if(value == 0 && tok == last){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + + outb(value,addr); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_WO(setreg); + +/** + * Show system led status - on/off/1hz/4hz + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return Hex string read from scratch register. + */ +static ssize_t sys_led_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + data = data & 0x3; + return sprintf(buf, "%s\n", + data == 0x03 ? "off" : data == 0x02 ? "4hz" : data ==0x01 ? "1hz": "on"); +} + +/** + * Set the status of system led - on/off/1hz/4hz + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t sys_led_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned char led_status,data; + if(sysfs_streq(buf, "off")){ + led_status = 0x03; + }else if(sysfs_streq(buf, "4hz")){ + led_status = 0x02; + }else if(sysfs_streq(buf, "1hz")){ + led_status = 0x01; + }else if(sysfs_streq(buf, "on")){ + led_status = 0x00; + }else{ + count = -EINVAL; + return count; + } + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + data = data & ~(0x3); + data = data | led_status; + outb(data, SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_RW(sys_led); + +/** + * Show system led color - both/green/yellow/none + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return Hex string read from scratch register. + */ +static ssize_t sys_led_color_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + data = (data >> 4) & 0x3; + return sprintf(buf, "%s\n", + data == 0x03 ? "off" : data == 0x02 ? "yellow" : data ==0x01 ? "green": "both"); +} + +/** + * Set the color of system led - both/green/yellow/none + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t sys_led_color_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned char led_status,data; + if(sysfs_streq(buf, "off")){ + led_status = 0x03; + }else if(sysfs_streq(buf, "yellow")){ + led_status = 0x02; + }else if(sysfs_streq(buf, "green")){ + led_status = 0x01; + }else if(sysfs_streq(buf, "both")){ + led_status = 0x00; + }else{ + count = -EINVAL; + return count; + } + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + data = data & ~( 0x3 << 4); + data = data | (led_status << 4); + outb(data, SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_RW(sys_led_color); + +static struct attribute *baseboard_cpld_attrs[] = { + &dev_attr_version.attr, + &dev_attr_scratch.attr, + &dev_attr_getreg.attr, + &dev_attr_setreg.attr, + &dev_attr_sys_led.attr, + &dev_attr_sys_led_color.attr, + NULL, +}; + +static struct attribute_group baseboard_cpld_attrs_grp = { + .attrs = baseboard_cpld_attrs, +}; + +static struct resource baseboard_cpld_resources[] = { + { + .start = 0xA100, + .end = 0xA1FF, + .flags = IORESOURCE_IO, + }, +}; + +static void baseboard_cpld_dev_release( struct device * dev) +{ + return; +} + +static struct platform_device baseboard_cpld_dev = { + .name = DRIVER_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(baseboard_cpld_resources), + .resource = baseboard_cpld_resources, + .dev = { + .release = baseboard_cpld_dev_release, + } +}; + +static int baseboard_cpld_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret =0; + + cpld_data = devm_kzalloc(&pdev->dev, sizeof(struct baseboard_cpld_data), + GFP_KERNEL); + if (!cpld_data) + return -ENOMEM; + + mutex_init(&cpld_data->cpld_lock); + + cpld_data->read_addr = VERSION_ADDR; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (unlikely(!res)) { + printk(KERN_ERR "Specified Resource Not Available...\n"); + return -1; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &baseboard_cpld_attrs_grp); + if (ret) { + printk(KERN_ERR "Cannot create sysfs for baseboard CPLD\n"); + } + return 0; +} + +static int baseboard_cpld_drv_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &baseboard_cpld_attrs_grp); + return 0; +} + +static struct platform_driver baseboard_cpld_drv = { + .probe = baseboard_cpld_drv_probe, + .remove = __exit_p(baseboard_cpld_drv_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +int baseboard_cpld_init(void) +{ + // Register platform device and platform driver + platform_device_register(&baseboard_cpld_dev); + platform_driver_register(&baseboard_cpld_drv); + return 0; +} + +void baseboard_cpld_exit(void) +{ + // Unregister platform device and platform driver + platform_driver_unregister(&baseboard_cpld_drv); + platform_device_unregister(&baseboard_cpld_dev); +} + +module_init(baseboard_cpld_init); +module_exit(baseboard_cpld_exit); + +MODULE_AUTHOR("Pradchaya Phucharoen "); +MODULE_DESCRIPTION("Celestica Seastone2 Baseboard CPLD Driver"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/mc24lc64t.c b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/mc24lc64t.c new file mode 100644 index 000000000000..fc15bb74b68d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/mc24lc64t.c @@ -0,0 +1,174 @@ +/* + * mc24lc64t.c - driver for Microchip 24LC64T + * + * Copyright (C) 2017 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define EEPROM_SIZE 8192 //mc24lt64t eeprom size in bytes. + +struct mc24lc64t_data { + struct mutex update_lock; +}; + +static ssize_t mc24lc64t_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = kobj_to_i2c_client(kobj); + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + unsigned long timeout, read_time, i = 0; + int status; + + mutex_lock(&drvdata->update_lock); + + if (i2c_smbus_write_byte_data(client, off>>8, off)) + { + status = -EIO; + goto exit; + } + + msleep(1); + +begin: + + if (i < count) + { + timeout = jiffies + msecs_to_jiffies(25); /* 25 mS timeout*/ + do { + read_time = jiffies; + + status = i2c_smbus_read_byte(client); + if (status >= 0) + { + buf[i++] = status; + goto begin; + } + } while (time_before(read_time, timeout)); + + status = -ETIMEDOUT; + goto exit; + } + + status = count; + +exit: + mutex_unlock(&drvdata->update_lock); + + return status; +} + + +static ssize_t mc24lc64t_write (struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count){ + + struct i2c_client *client = kobj_to_i2c_client(kobj); + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + unsigned long timeout, write_time, i = 0; + int status; + u16 value; + + mutex_lock(&drvdata->update_lock); + +begin: + if (i < count){ + timeout = jiffies + msecs_to_jiffies(25); /* 25 mS timeout*/ + value = (buf[i] << 8 | ( off &0xff)); + do { + write_time = jiffies; + status = i2c_smbus_write_word_data(client, off>>8, value); + if (status >= 0) + { + // increase offset + off++; + // increase buffer index + i++; + goto begin; + } + } while (time_before(write_time, timeout)); + status = -ETIMEDOUT; + goto exit; + } + status = count; + +exit: + mutex_unlock(&drvdata->update_lock); + return status; +} + + +static struct bin_attribute mc24lc64t_bit_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO | S_IWUGO, + }, + .size = EEPROM_SIZE, + .read = mc24lc64t_read, + .write = mc24lc64t_write, +}; + +static int mc24lc64t_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct mc24lc64t_data *drvdata; + int err; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA + | I2C_FUNC_SMBUS_READ_BYTE)) + return -EPFNOSUPPORT; + + if (!(drvdata = devm_kzalloc(&client->dev, + sizeof(struct mc24lc64t_data), GFP_KERNEL))) + return -ENOMEM; + + i2c_set_clientdata(client, drvdata); + mutex_init(&drvdata->update_lock); + + err = sysfs_create_bin_file(&client->dev.kobj, &mc24lc64t_bit_attr); + return err; +} + +static int mc24lc64t_remove(struct i2c_client *client) +{ + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + sysfs_remove_bin_file(&client->dev.kobj, &mc24lc64t_bit_attr); + + return 0; +} + +static const struct i2c_device_id mc24lc64t_id[] = { + { "24lc64t", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mc24lc64t_id); + +static struct i2c_driver mc24lc64t_driver = { + .driver = { + .name = "mc24lc64t", + .owner = THIS_MODULE, + }, + .probe = mc24lc64t_probe, + .remove = mc24lc64t_remove, + .id_table = mc24lc64t_id, +}; + +module_i2c_driver(mc24lc64t_driver); + +MODULE_AUTHOR("Abhisit Sangjan "); +MODULE_DESCRIPTION("Microchip 24LC64T Driver"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/switchboard_fpga.c b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/switchboard_fpga.c new file mode 100644 index 000000000000..5918d27f3911 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/switchboard_fpga.c @@ -0,0 +1,2365 @@ +/* + * switchboard_fpga.c - driver for seastone2 Switch board FPGA/CPLD. + * + * Author: Pradchaya Phucharoen + * + * Copyright (C) 2019 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * / + * \--sys + * \--devices + * \--platform + * \--switchboard + * |--FPGA + * |--CPLD1 + * |--CPLD2 + * \--SFF + * |--QSFP[1..32] + * \--SFP1 + * + */ + +#ifndef TEST_MODE +#define MOD_VERSION "2.2.0" +#else +#define MOD_VERSION "TEST" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int majorNumber; + +#define CLASS_NAME "seastone2_fpga" +#define DRIVER_NAME "switchboard" +#define FPGA_PCI_NAME "Seastone2_fpga_pci" +#define DEVICE_NAME "fwupgrade" + + +static int smbus_access(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data); + +static int fpga_i2c_access(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data); + +static int fpgafw_init(void); +static void fpgafw_exit(void); + +/* +======================================== +FPGA PCIe BAR 0 Registers +======================================== +Misc Control 0x00000000 – 0x000000FF. +I2C_CH1 0x00000100 - 0x00000110 +I2C_CH2 0x00000200 - 0x00000210. +I2C_CH3 0x00000300 - 0x00000310. +I2C_CH4 0x00000400 - 0x00000410. +I2C_CH5 0x00000500 - 0x00000510. +I2C_CH6 0x00000600 - 0x00000610. +I2C_CH7 0x00000700 - 0x00000710. +I2C_CH8 0x00000800 - 0x00000810. +I2C_CH9 0x00000900 - 0x00000910. +I2C_CH10 0x00000A00 - 0x00000A10. +SPI Master 0x00001200 - 0x00001300. +PORT XCVR 0x00004000 - 0x00004FFF. +*/ + +/* MISC */ +#define FPGA_VERSION 0x0000 +#define FPGA_VERSION_MJ_MSK 0xff00 +#define FPGA_VERSION_MN_MSK 0x00ff +#define FPGA_SCRATCH 0x0004 +#define FPGA_BROAD_TYPE 0x0008 +#define FPGA_BROAD_REV_MSK 0x0038 +#define FPGA_BROAD_ID_MSK 0x0007 +#define FPGA_PLL_STATUS 0x0014 +#define BMC_I2C_SCRATCH 0x0020 +#define FPGA_SLAVE_CPLD_REST 0x0030 +#define FPGA_PERIPH_RESET_CTRL 0x0034 +#define FPGA_INT_STATUS 0x0040 +#define FPGA_INT_SRC_STATUS 0x0044 +#define FPGA_INT_FLAG 0x0048 +#define FPGA_INT_MASK 0x004c +#define FPGA_MISC_CTRL 0x0050 +#define FPGA_MISC_STATUS 0x0054 +#define FPGA_AVS_VID_STATUS 0x0068 +#define FPGA_FEATURE_CARD_GPIO 0x0070 +#define FPGA_PORT_XCVR_READY 0x000c + +/* I2C_MASTER BASE ADDR */ +#define I2C_MASTER_FREQ_1 0x0100 +#define I2C_MASTER_CTRL_1 0x0104 +#define I2C_MASTER_STATUS_1 0x0108 +#define I2C_MASTER_DATA_1 0x010c +#define I2C_MASTER_PORT_ID_1 0x0110 +#define I2C_MASTER_CH_1 1 +#define I2C_MASTER_CH_2 2 +#define I2C_MASTER_CH_3 3 +#define I2C_MASTER_CH_4 4 +#define I2C_MASTER_CH_5 5 +#define I2C_MASTER_CH_6 6 +#define I2C_MASTER_CH_7 7 +#define I2C_MASTER_CH_8 8 +#define I2C_MASTER_CH_9 9 +#define I2C_MASTER_CH_10 10 +#define I2C_MASTER_CH_TOTAL I2C_MASTER_CH_10 + +/* SPI_MASTER */ +#define SPI_MASTER_WR_EN 0x1200 /* one bit */ +#define SPI_MASTER_WR_DATA 0x1204 /* 32 bits */ +#define SPI_MASTER_CHK_ID 0x1208 /* one bit */ +#define SPI_MASTER_VERIFY 0x120c /* one bit */ +#define SPI_MASTER_STATUS 0x1210 /* 15 bits */ +#define SPI_MASTER_MODULE_RST 0x1214 /* one bit */ + +/* FPGA FRONT PANEL PORT MGMT */ +#define SFF_PORT_CTRL_BASE 0x4000 +#define SFF_PORT_STATUS_BASE 0x4004 +#define SFF_PORT_INT_STATUS_BASE 0x4008 +#define SFF_PORT_INT_MASK_BASE 0x400c + +#define PORT_XCVR_REGISTER_SIZE 0x1000 + +/* PORT CTRL REGISTER +[31:7] RSVD +[6] LPMOD 6 +[5] RSVD +[4] RST 4 +[3:1] RSVD +[0] TXDIS 0 +*/ +#define CTRL_LPMOD 6 +#define CTRL_RST 4 +#define CTRL_TXDIS 0 + +/* PORT STATUS REGISTER +[31:6] RSVD +[5] IRQ 5 +[4] PRESENT 4 +[3] RSVD +[2] TXFAULT 2 +[1] RXLOS 1 +[0] MODABS 0 +*/ +#define STAT_IRQ 5 +#define STAT_PRESENT 4 +#define STAT_TXFAULT 2 +#define STAT_RXLOS 1 +#define STAT_MODABS 0 + +/* PORT INTRPT REGISTER +[31:6] RSVD +[5] INT_N 5 +[4] PRESENT 4 +[3] RSVD +[2] RSVD +[1] RXLOS 1 +[0] MODABS 0 +*/ +#define INTR_INT_N 5 +#define INTR_PRESENT 4 +#define INTR_RXLOS 1 +#define INTR_MODABS 0 + +/* PORT INT MASK REGISTER +[31:6] RSVD +[5] INT_N 5 +[4] PRESENT 4 +[3] RSVD +[2] RSVD +[1] RXLOS_INT 1 +[0] MODABS 0 +*/ +#define MASK_INT_N 5 +#define MASK_PRESENT 4 +#define MASK_RXLOS 1 +#define MASK_MODABS 0 + +enum { + I2C_SR_BIT_RXAK = 0, + I2C_SR_BIT_MIF, + I2C_SR_BIT_SRW, + I2C_SR_BIT_BCSTM, + I2C_SR_BIT_MAL, + I2C_SR_BIT_MBB, + I2C_SR_BIT_MAAS, + I2C_SR_BIT_MCF +}; + +enum { + I2C_CR_BIT_BCST = 0, + I2C_CR_BIT_RSTA = 2, + I2C_CR_BIT_TXAK, + I2C_CR_BIT_MTX, + I2C_CR_BIT_MSTA, + I2C_CR_BIT_MIEN, + I2C_CR_BIT_MEN, +}; + +/** + * + * The function is i2c algorithm implement to allow master access to + * correct endpoint devices trough the PCA9548 switch devices. + * + * FPGA I2C Master [mutex resource] + * | + * | + * --------------------------- + * | PCA9548(s) | + * ---1--2--3--4--5--6--7--8-- + * | | | | | | | | + * EEPROM ... EEPROM + * + */ + + +#define VIRTUAL_I2C_QSFP_PORT 32 +#define VIRTUAL_I2C_SFP_PORT 1 + +#define SFF_PORT_TOTAL VIRTUAL_I2C_QSFP_PORT + VIRTUAL_I2C_SFP_PORT + +#define VIRTUAL_I2C_BUS_OFFSET 2 +#define CPLD1_SLAVE_ADDR 0x30 +#define CPLD2_SLAVE_ADDR 0x31 + +static struct class* fpgafwclass = NULL; ///< The device-driver class struct pointer +static struct device* fpgafwdev = NULL; ///< The device-driver device struct pointer +static struct platform_device *seastone2_dev; + +#define PCI_VENDOR_ID_TEST 0x1af4 + +#ifndef PCI_VENDOR_ID_XILINX +#define PCI_VENDOR_ID_XILINX 0x10EE +#endif + +#define FPGA_PCIE_DEVICE_ID 0x7021 +#define TEST_PCIE_DEVICE_ID 0x1110 + + +#ifdef DEBUG_KERN +#define info(fmt,args...) printk(KERN_INFO "line %3d : "fmt,__LINE__,##args) +#define check(REG) printk(KERN_INFO "line %3d : %-8s = %2.2X",__LINE__,#REG,ioread8(REG)); +#else +#define info(fmt,args...) +#define check(REG) +#endif + +#define GET_REG_BIT(REG,BIT) ((ioread8(REG) >> BIT) & 0x01) +#define SET_REG_BIT_H(REG,BIT) iowrite8(ioread8(REG) | (0x01 << BIT),REG) +#define SET_REG_BIT_L(REG,BIT) iowrite8(ioread8(REG) & ~(0x01 << BIT),REG) + +static struct mutex fpga_i2c_master_locks[I2C_MASTER_CH_TOTAL]; +/* Store lasted switch address and channel */ +static uint16_t fpga_i2c_lasted_access_port[I2C_MASTER_CH_TOTAL]; + +enum PORT_TYPE { + NONE, + QSFP, + SFP +}; + +struct i2c_switch { + unsigned char master_bus; // I2C bus number + unsigned char switch_addr; // PCA9548 device address, 0xFF if directly connect to a bus. + unsigned char channel; // PCA9548 channel number. If the switch_addr is 0xFF, this value is ignored. + enum PORT_TYPE port_type; // QSFP/SFP tranceiver port type. + char calling_name[20]; // Calling name. +}; + +struct i2c_dev_data { + int portid; + struct i2c_switch pca9548; +}; + +/* PREDEFINED I2C SWITCH DEVICE TOPOLOGY */ +static struct i2c_switch fpga_i2c_bus_dev[] = { + /* BUS2 QSFP Exported as virtual bus */ + {I2C_MASTER_CH_2, 0x72, 0, QSFP, "QSFP1"}, {I2C_MASTER_CH_2, 0x72, 1, QSFP, "QSFP2"}, + {I2C_MASTER_CH_2, 0x72, 2, QSFP, "QSFP3"}, {I2C_MASTER_CH_2, 0x72, 3, QSFP, "QSFP4"}, + {I2C_MASTER_CH_2, 0x72, 4, QSFP, "QSFP5"}, {I2C_MASTER_CH_2, 0x72, 5, QSFP, "QSFP6"}, + {I2C_MASTER_CH_2, 0x72, 6, QSFP, "QSFP7"}, {I2C_MASTER_CH_2, 0x72, 7, QSFP, "QSFP8"}, + {I2C_MASTER_CH_2, 0x73, 0, QSFP, "QSFP9"}, {I2C_MASTER_CH_2, 0x73, 1, QSFP, "QSFP10"}, + {I2C_MASTER_CH_2, 0x73, 2, QSFP, "QSFP11"}, {I2C_MASTER_CH_2, 0x73, 3, QSFP, "QSFP12"}, + {I2C_MASTER_CH_2, 0x73, 4, QSFP, "QSFP13"}, {I2C_MASTER_CH_2, 0x73, 5, QSFP, "QSFP14"}, + {I2C_MASTER_CH_2, 0x73, 6, QSFP, "QSFP15"}, {I2C_MASTER_CH_2, 0x73, 7, QSFP, "QSFP16"}, + {I2C_MASTER_CH_2, 0x74, 0, QSFP, "QSFP17"}, {I2C_MASTER_CH_2, 0x74, 1, QSFP, "QSFP18"}, + {I2C_MASTER_CH_2, 0x74, 2, QSFP, "QSFP19"}, {I2C_MASTER_CH_2, 0x74, 3, QSFP, "QSFP20"}, + {I2C_MASTER_CH_2, 0x74, 4, QSFP, "QSFP21"}, {I2C_MASTER_CH_2, 0x74, 5, QSFP, "QSFP22"}, + {I2C_MASTER_CH_2, 0x74, 6, QSFP, "QSFP23"}, {I2C_MASTER_CH_2, 0x74, 7, QSFP, "QSFP24"}, + {I2C_MASTER_CH_2, 0x75, 0, QSFP, "QSFP25"}, {I2C_MASTER_CH_2, 0x75, 1, QSFP, "QSFP26"}, + {I2C_MASTER_CH_2, 0x75, 2, QSFP, "QSFP27"}, {I2C_MASTER_CH_2, 0x75, 3, QSFP, "QSFP28"}, + {I2C_MASTER_CH_2, 0x75, 4, QSFP, "QSFP29"}, {I2C_MASTER_CH_2, 0x75, 5, QSFP, "QSFP30"}, + {I2C_MASTER_CH_2, 0x75, 6, QSFP, "QSFP31"}, {I2C_MASTER_CH_2, 0x75, 7, QSFP, "QSFP32"}, + /* BUS1 SFP+ Exported as virtual bus */ + {I2C_MASTER_CH_1, 0x72, 0, SFP, "SFP1"}, + /* BUS3 Switchboard CPLD */ + {I2C_MASTER_CH_3, 0xFF, 0, NONE, "I2C_3"}, +}; + +#define VIRTUAL_I2C_PORT_LENGTH ARRAY_SIZE(fpga_i2c_bus_dev) +#define VIRTUAL_I2C_CPLD_INDEX SFF_PORT_TOTAL + +struct fpga_device { + /* data mmio region */ + void __iomem *data_base_addr; + resource_size_t data_mmio_start; + resource_size_t data_mmio_len; +}; + +static struct fpga_device fpga_dev = { + .data_base_addr = 0, + .data_mmio_start = 0, + .data_mmio_len = 0, +}; + +struct seastone2_fpga_data { + struct device *sff_devices[SFF_PORT_TOTAL]; + struct i2c_client *sff_i2c_clients[SFF_PORT_TOTAL]; + struct i2c_adapter *i2c_adapter[VIRTUAL_I2C_PORT_LENGTH]; + struct mutex fpga_lock; // For FPGA internal lock + void __iomem * fpga_read_addr; + uint8_t cpld1_read_addr; + uint8_t cpld2_read_addr; +}; + +struct sff_device_data { + int portid; + enum PORT_TYPE port_type; +}; + +struct seastone2_fpga_data *fpga_data; + +/* + * Kernel object for other module drivers. + * Other module can use these kobject as a parent. + */ + +static struct kobject *fpga = NULL; +static struct kobject *cpld1 = NULL; +static struct kobject *cpld2 = NULL; + +/** + * Device node in sysfs tree. + */ +static struct device *sff_dev = NULL; + +/** + * Show the value of the register set by 'set_fpga_reg_address' + * If the address is not set by 'set_fpga_reg_address' first, + * The version register is selected by default. + * @param buf register value in hextring + * @return number of bytes read, or an error code + */ +static ssize_t get_fpga_reg_value(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // read data from the address + uint32_t data; + data = ioread32(fpga_data->fpga_read_addr); + return sprintf(buf, "0x%8.8x\n", data); +} + +/** + * Store the register address + * @param buf address wanted to be read value of + * @return number of bytes stored, or an error code + */ +static ssize_t set_fpga_reg_address(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + uint32_t addr; + + status = kstrtou32(buf, 0, &addr); + if (status == 0) { + fpga_data->fpga_read_addr = fpga_dev.data_base_addr + addr; + status = count; + } + return status; +} + +/** + * Show value of fpga scratch register + * @param buf register value in hexstring + * @return number of bytes read, or an error code + */ +static ssize_t get_fpga_scratch(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint32_t data; + data = ioread32(fpga_dev.data_base_addr + FPGA_SCRATCH); + data &= 0xffffffff; + return sprintf(buf, "0x%8.8x\n", data); +} + +/** + * Store value of fpga scratch register + * @param buf scratch register value passing from user space + * @return number of bytes stored, or an error code + */ +static ssize_t set_fpga_scratch(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + uint32_t data; + + status = kstrtou32(buf, 0, &data); + if (status == 0) { + iowrite32(data, fpga_dev.data_base_addr + FPGA_SCRATCH); + status = count; + } + return status; +} + +/** + * Store a value in a specific register address + * @param buf the value and address in format '0xhhhh 0xhhhhhhhh' + * @return number of bytes sent by user space, or an error code + */ +static ssize_t set_fpga_reg_value(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + //register is 4 bytes + uint32_t addr; + uint32_t value; + uint32_t mode = 8; + char *tok; + char clone[count]; + char *pclone = clone; + ssize_t status; + + strcpy(clone, buf); + + mutex_lock(&fpga_data->fpga_lock); + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + status = kstrtou32(tok, 0, &addr); + if (status != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + status = kstrtou32(tok, 0, &value); + if (status != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + mode = 32; + } else { + status = kstrtou32(tok, 0, &mode); + if (status != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + } + if (mode == 32) { + iowrite32(value, fpga_dev.data_base_addr + addr); + } else if (mode == 8) { + iowrite8(value, fpga_dev.data_base_addr + addr); + } else { + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + mutex_unlock(&fpga_data->fpga_lock); + return count; +} + +/** + * Show FPGA port XCVR ready status + */ +static ssize_t ready_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u32 data; + unsigned int REGISTER = FPGA_PORT_XCVR_READY; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> 0) & 1U); +} + +/* FPGA attributes */ +static DEVICE_ATTR( getreg, 0600, get_fpga_reg_value, set_fpga_reg_address); +static DEVICE_ATTR( scratch, 0600, get_fpga_scratch, set_fpga_scratch); +static DEVICE_ATTR( setreg, 0200, NULL , set_fpga_reg_value); +static DEVICE_ATTR_RO(ready); + +static struct attribute *fpga_attrs[] = { + &dev_attr_getreg.attr, + &dev_attr_scratch.attr, + &dev_attr_setreg.attr, + &dev_attr_ready.attr, + NULL, +}; + +static struct attribute_group fpga_attr_grp = { + .attrs = fpga_attrs, +}; + +/* SW CPLDs attributes */ +static ssize_t cpld1_getreg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + uint8_t data; + int err; + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, + I2C_SMBUS_READ, fpga_data->cpld1_read_addr, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data); + + if (err < 0) + return err; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t cpld1_getreg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + uint8_t addr; + + status = kstrtou8(buf, 0, &addr); + if (status == 0) { + fpga_data->cpld1_read_addr = addr; + status = count; + } + return status; +} + +static ssize_t cpld1_scratch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + uint8_t data; + int err; + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x01, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data); + if (err < 0) + return err; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t cpld1_scratch_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + // CPLD register is one byte + uint8_t data; + ssize_t status; + int err; + + status = kstrtou8(buf, 0, &data); + if (status != 0) { + return status; + } + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, 0x01, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data); + if (err < 0) + return err; + + return count; +} + +static ssize_t cpld1_setreg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + uint8_t addr, value; + char *tok; + char clone[count]; + char *pclone = clone; + ssize_t status; + int err; + + strcpy(clone, buf); + + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + return -EINVAL; + } + status = kstrtou8(tok, 0, &addr); + if (status != 0) { + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + return -EINVAL; + } + status = kstrtou8(tok, 0, &value); + if (status != 0) { + return -EINVAL; + } + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, addr, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&value); + if (err < 0) + return err; + + return count; +} + +struct device_attribute dev_attr_cpld1_getreg = __ATTR(getreg, 0600, cpld1_getreg_show, cpld1_getreg_store); +struct device_attribute dev_attr_cpld1_scratch = __ATTR(scratch, 0600, cpld1_scratch_show, cpld1_scratch_store); +struct device_attribute dev_attr_cpld1_setreg = __ATTR(setreg, 0200, NULL, cpld1_setreg_store); + +static struct attribute *cpld1_attrs[] = { + &dev_attr_cpld1_getreg.attr, + &dev_attr_cpld1_scratch.attr, + &dev_attr_cpld1_setreg.attr, + NULL, +}; + +static struct attribute_group cpld1_attr_grp = { + .attrs = cpld1_attrs, +}; + +static ssize_t cpld2_getreg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + uint8_t data; + int err; + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, fpga_data->cpld2_read_addr, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data); + if (err < 0) + return err; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t cpld2_getreg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + // CPLD register is one byte + uint8_t addr; + ssize_t status; + + status = kstrtou8(buf, 0, &addr); + if (status == 0) { + fpga_data->cpld2_read_addr = addr; + status = count; + } + return status; +} + +static ssize_t cpld2_scratch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + uint8_t data; + int err; + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x01, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data); + if (err < 0) + return err; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t cpld2_scratch_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + // CPLD register is one byte + uint8_t data; + int err; + ssize_t status; + + status = kstrtou8(buf, 0, &data); + if (status != 0) { + return -EINVAL; + } + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, 0x01, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data); + if (err < 0) + return err; + + return count; +} + +static ssize_t cpld2_setreg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t addr, value; + char *tok; + char clone[count]; + char *pclone = clone; + ssize_t status; + int err; + + strcpy(clone, buf); + + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + return -EINVAL; + } + status = kstrtou8(tok, 0, &addr); + if (status != 0) { + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + return -EINVAL; + } + status = kstrtou8(tok, 0, &value); + if (status != 0) { + return -EINVAL; + } + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, addr, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&value); + if (err < 0) + return err; + + return count; +} + +struct device_attribute dev_attr_cpld2_getreg = __ATTR(getreg, 0600, cpld2_getreg_show, cpld2_getreg_store); +struct device_attribute dev_attr_cpld2_scratch = __ATTR(scratch, 0600, cpld2_scratch_show, cpld2_scratch_store); +struct device_attribute dev_attr_cpld2_setreg = __ATTR(setreg, 0200, NULL, cpld2_setreg_store); + +static struct attribute *cpld2_attrs[] = { + &dev_attr_cpld2_getreg.attr, + &dev_attr_cpld2_scratch.attr, + &dev_attr_cpld2_setreg.attr, + NULL, +}; + +static struct attribute_group cpld2_attr_grp = { + .attrs = cpld2_attrs, +}; + +/* QSFP/SFP+ attributes */ +static ssize_t qsfp_modirq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> STAT_IRQ) & 1U); +} +DEVICE_ATTR_RO(qsfp_modirq); + +static ssize_t qsfp_modprs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> STAT_PRESENT) & 1U); +} +DEVICE_ATTR_RO(qsfp_modprs); + +static ssize_t sfp_txfault_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> STAT_TXFAULT) & 1U); +} +DEVICE_ATTR_RO(sfp_txfault); + +static ssize_t sfp_rxlos_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> STAT_RXLOS) & 1U); +} +DEVICE_ATTR_RO(sfp_rxlos); + +static ssize_t sfp_modabs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> STAT_MODABS) & 1U); +} +DEVICE_ATTR_RO(sfp_modabs); + +static ssize_t qsfp_lpmode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> CTRL_LPMOD) & 1U); +} +static ssize_t qsfp_lpmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + uint32_t value; + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtou32(buf, 0, &value); + if (status == 0) { + // check if value is 0 clear + data = ioread32(fpga_dev.data_base_addr + REGISTER); + if (!value) + data = data & ~( (u32)0x1 << CTRL_LPMOD); + else + data = data | ((u32)0x1 << CTRL_LPMOD); + iowrite32(data, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(qsfp_lpmode); + +static ssize_t qsfp_reset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> CTRL_RST) & 1U); +} + +static ssize_t qsfp_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + uint32_t value; + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtou32(buf, 0, &value); + if (status == 0) { + // check if value is 0 clear + data = ioread32(fpga_dev.data_base_addr + REGISTER); + if (!value) + data = data & ~( (u32)0x1 << CTRL_RST); + else + data = data | ((u32)0x1 << CTRL_RST); + iowrite32(data, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(qsfp_reset); + +static ssize_t qsfp_isr_flags_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT); + + mutex_lock(&fpga_data->fpga_lock); + data = (u8) ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + + /* + * Unify the return pattern to 2-bit + * [1] : module interrupt + * [0] : presence + */ + data = data & valid_bits; + data = data >> 4; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t qsfp_isr_flags_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + u32 value; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT); + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtou32(buf, 0, &value); + if (status == 0) { + value = value << 4; + value = value & valid_bits; + iowrite32(value, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(qsfp_isr_flags); + +static ssize_t qsfp_isr_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT); + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + + /* + * Unify the return pattern to 2-bit + * [1] : module interrupt + * [0] : presence + */ + data = data & valid_bits; + data = data >> 4; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t qsfp_isr_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + u32 value; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT); + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtou32(buf, 0, &value); + if (status == 0) { + value = value << 4; + value = value & valid_bits; + iowrite32(value, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(qsfp_isr_mask); + +static ssize_t sfp_txdisable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> CTRL_TXDIS) & 1U); +} +static ssize_t sfp_txdisable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + long value; + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtol(buf, 0, &value); + if (status == 0) { + // check if value is 0 clear + data = ioread32(fpga_dev.data_base_addr + REGISTER); + if (!value) + data = data & ~( (u32)0x1 << CTRL_TXDIS); + else + data = data | ((u32)0x1 << CTRL_TXDIS); + iowrite32(data, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(sfp_txdisable); + +static ssize_t sfp_isr_flags_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_RXLOS) | BIT(INTR_MODABS); + + mutex_lock(&fpga_data->fpga_lock); + data = (u8) ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + + data = data & valid_bits; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t sfp_isr_flags_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + u32 value; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT); + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtou32(buf, 0, &value); + if (status == 0) { + value = value & valid_bits; + iowrite32(value, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(sfp_isr_flags); + +static ssize_t sfp_isr_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_RXLOS) | BIT(INTR_MODABS); + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + + data = data & valid_bits; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t sfp_isr_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + u32 value; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_RXLOS) | BIT(INTR_MODABS); + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtou32(buf, 0, &value); + if (status == 0) { + value = value & valid_bits; + iowrite32(value, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(sfp_isr_mask); + +static struct attribute *sff_attrs[] = { + &dev_attr_qsfp_modirq.attr, + &dev_attr_qsfp_modprs.attr, + &dev_attr_qsfp_lpmode.attr, + &dev_attr_qsfp_reset.attr, + &dev_attr_qsfp_isr_flags.attr, + &dev_attr_qsfp_isr_mask.attr, + &dev_attr_sfp_txfault.attr, + &dev_attr_sfp_rxlos.attr, + &dev_attr_sfp_modabs.attr, + &dev_attr_sfp_txdisable.attr, + &dev_attr_sfp_isr_flags.attr, + &dev_attr_sfp_isr_mask.attr, + NULL, +}; + +static struct attribute_group sff_attr_grp = { + .attrs = sff_attrs, +}; + +static const struct attribute_group *sff_attr_grps[] = { + &sff_attr_grp, + NULL +}; + + +static ssize_t port_led_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // value can be "nomal", "test" + __u8 led_mode_1, led_mode_2; + int err; + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x09, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_mode_1); + if (err < 0) + return err; + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x09, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_mode_2); + if (err < 0) + return err; + + return sprintf(buf, "%s %s\n", + led_mode_1 ? "test" : "normal", + led_mode_2 ? "test" : "normal"); +} +static ssize_t port_led_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int status; + __u8 led_mode_1; + if (sysfs_streq(buf, "test")) { + led_mode_1 = 0x01; + } else if (sysfs_streq(buf, "normal")) { + led_mode_1 = 0x00; + } else { + return -EINVAL; + } + status = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, 0x09, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_mode_1); + status = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, 0x09, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_mode_1); + return count; +} +DEVICE_ATTR_RW(port_led_mode); + +// Only work when port_led_mode set to 1 +static ssize_t port_led_color_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // value can be "off", "green", "amber", "both" + __u8 led_color1, led_color2; + int err; + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x0A, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_color1); + if (err < 0) + return err; + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x0A, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_color2); + if (err < 0) + return err; + return sprintf(buf, "%s %s\n", + led_color1 == 0x03 ? "off" : led_color1 == 0x02 ? "green" : led_color1 == 0x01 ? "amber" : "both", + led_color2 == 0x03 ? "off" : led_color2 == 0x02 ? "green" : led_color2 == 0x01 ? "amber" : "both"); +} + +static ssize_t port_led_color_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int status; + __u8 led_color; + if (sysfs_streq(buf, "off")) { + led_color = 0x03; + } else if (sysfs_streq(buf, "green")) { + led_color = 0x02; + } else if (sysfs_streq(buf, "amber")) { + led_color = 0x01; + } else if (sysfs_streq(buf, "both")) { + led_color = 0x00; + } else { + status = -EINVAL; + return status; + } + status = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, + I2C_SMBUS_WRITE, 0x0A, I2C_SMBUS_BYTE_DATA, + (union i2c_smbus_data*)&led_color); + status = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, + I2C_SMBUS_WRITE, 0x0A, I2C_SMBUS_BYTE_DATA, + (union i2c_smbus_data*)&led_color); + return count; +} +DEVICE_ATTR_RW(port_led_color); + +static struct attribute *sff_led_test[] = { + &dev_attr_port_led_mode.attr, + &dev_attr_port_led_color.attr, + NULL, +}; + +static struct attribute_group sff_led_test_grp = { + .attrs = sff_led_test, +}; + +static struct device * seastone2_sff_init(int portid) { + struct sff_device_data *new_data; + struct device *new_device; + + new_data = kzalloc(sizeof(*new_data), GFP_KERNEL); + if (!new_data) { + printk(KERN_ALERT "Cannot alloc sff device data @port%d", portid); + return NULL; + } + /* The QSFP port ID start from 1 */ + new_data->portid = portid + 1; + new_data->port_type = fpga_i2c_bus_dev[portid].port_type; + new_device = device_create_with_groups(fpgafwclass, sff_dev, + MKDEV(0, 0), new_data, sff_attr_grps, "%s", + fpga_i2c_bus_dev[portid].calling_name); + if (IS_ERR(new_device)) { + printk(KERN_ALERT "Cannot create sff device @port%d", portid); + kfree(new_data); + return NULL; + } + return new_device; +} + +static int i2c_wait_ack(struct i2c_adapter *a, unsigned long timeout, int writing) { + int error = 0; + int Status; + + struct i2c_dev_data *new_data = i2c_get_adapdata(a); + void __iomem *pci_bar = fpga_dev.data_base_addr; + + unsigned int REG_FDR0; + unsigned int REG_CR0; + unsigned int REG_SR0; + unsigned int REG_DR0; + unsigned int REG_ID0; + + unsigned int master_bus = new_data->pca9548.master_bus; + + if (master_bus < I2C_MASTER_CH_1 || master_bus > I2C_MASTER_CH_TOTAL) { + error = -EINVAL; + return error; + } + + REG_FDR0 = I2C_MASTER_FREQ_1 + (master_bus - 1) * 0x0100; + REG_CR0 = I2C_MASTER_CTRL_1 + (master_bus - 1) * 0x0100; + REG_SR0 = I2C_MASTER_STATUS_1 + (master_bus - 1) * 0x0100; + REG_DR0 = I2C_MASTER_DATA_1 + (master_bus - 1) * 0x0100; + REG_ID0 = I2C_MASTER_PORT_ID_1 + (master_bus - 1) * 0x0100; + + check(pci_bar + REG_SR0); + check(pci_bar + REG_CR0); + + timeout = jiffies + msecs_to_jiffies(timeout); + while (1) { + Status = ioread8(pci_bar + REG_SR0); + if (jiffies > timeout) { + info("Status %2.2X", Status); + info("Error Timeout"); + error = -ETIMEDOUT; + break; + } + + + if (Status & (1 << I2C_SR_BIT_MIF)) { + break; + } + + if (writing == 0 && (Status & (1 << I2C_SR_BIT_MCF))) { + break; + } + } + Status = ioread8(pci_bar + REG_SR0); + iowrite8(0, pci_bar + REG_SR0); + + if (error < 0) { + info("Status %2.2X", Status); + return error; + } + + if (!(Status & (1 << I2C_SR_BIT_MCF))) { + info("Error Unfinish"); + return -EIO; + } + + if (Status & (1 << I2C_SR_BIT_MAL)) { + info("Error MAL"); + return -EAGAIN; + } + + if (Status & (1 << I2C_SR_BIT_RXAK)) { + info( "SL No Acknowlege"); + if (writing) { + info("Error No Acknowlege"); + iowrite8(1 << I2C_CR_BIT_MEN, pci_bar + REG_CR0); + return -ENXIO; + } + } else { + info( "SL Acknowlege"); + } + + return 0; +} + +static int smbus_access(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data) +{ + int error = 0; + int cnt = 0; + int bid = 0; + struct i2c_dev_data *dev_data; + void __iomem *pci_bar; + unsigned int portid, master_bus; + + unsigned int REG_FDR0; + unsigned int REG_CR0; + unsigned int REG_SR0; + unsigned int REG_DR0; + unsigned int REG_ID0; + + REG_FDR0 = 0; + REG_CR0 = 0; + REG_SR0 = 0; + REG_DR0 = 0; + REG_ID0 = 0; + + /* Write the command register */ + dev_data = i2c_get_adapdata(adapter); + portid = dev_data->portid; + pci_bar = fpga_dev.data_base_addr; + +#ifdef DEBUG_KERN + printk(KERN_INFO "portid %2d|@ 0x%2.2X|f 0x%4.4X|(%d)%-5s| (%d)%-15s|CMD %2.2X " + , portid, addr, flags, rw, rw == 1 ? "READ " : "WRITE" + , size, size == 0 ? "QUICK" : + size == 1 ? "BYTE" : + size == 2 ? "BYTE_DATA" : + size == 3 ? "WORD_DATA" : + size == 4 ? "PROC_CALL" : + size == 5 ? "BLOCK_DATA" : + size == 8 ? "I2C_BLOCK_DATA" : "ERROR" + , cmd); +#endif + /* Map the size to what the chip understands */ + switch (size) { + case I2C_SMBUS_QUICK: + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: + break; + default: + printk(KERN_INFO "Unsupported transaction %d\n", size); + error = -EOPNOTSUPP; + goto Done; + } + + master_bus = dev_data->pca9548.master_bus; + + if (master_bus < I2C_MASTER_CH_1 || master_bus > I2C_MASTER_CH_TOTAL) { + error = -ENXIO; + goto Done; + } + + REG_FDR0 = I2C_MASTER_FREQ_1 + (master_bus - 1) * 0x0100; + REG_CR0 = I2C_MASTER_CTRL_1 + (master_bus - 1) * 0x0100; + REG_SR0 = I2C_MASTER_STATUS_1 + (master_bus - 1) * 0x0100; + REG_DR0 = I2C_MASTER_DATA_1 + (master_bus - 1) * 0x0100; + REG_ID0 = I2C_MASTER_PORT_ID_1 + (master_bus - 1) * 0x0100; + + iowrite8(portid, pci_bar + REG_ID0); + + ////[S][ADDR/R] + // Clear status register + iowrite8(0, pci_bar + REG_SR0); + iowrite8(1 << I2C_CR_BIT_MIEN | + 1 << I2C_CR_BIT_MTX | + 1 << I2C_CR_BIT_MSTA , pci_bar + REG_CR0); + SET_REG_BIT_H(pci_bar + REG_CR0, I2C_CR_BIT_MEN); + + if (rw == I2C_SMBUS_READ && + (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE)) { + // sent device address with Read mode + iowrite8(addr << 1 | 0x01, pci_bar + REG_DR0); + } else { + // sent device address with Write mode + iowrite8(addr << 1 | 0x00, pci_bar + REG_DR0); + } + + + + info( "MS Start"); + + //// Wait {A} + error = i2c_wait_ack(adapter, 12, 1); + if (error < 0) { + info( "get error %d", error); + goto Done; + } + + //// [CMD]{A} + if (size == I2C_SMBUS_BYTE_DATA || + size == I2C_SMBUS_WORD_DATA || + size == I2C_SMBUS_BLOCK_DATA || + size == I2C_SMBUS_I2C_BLOCK_DATA || + (size == I2C_SMBUS_BYTE && rw == I2C_SMBUS_WRITE)) { + + // sent command code to data register + iowrite8(cmd, pci_bar + REG_DR0); + info( "MS Send CMD 0x%2.2X", cmd); + + // Wait {A} + error = i2c_wait_ack(adapter, 12, 1); + if (error < 0) { + info( "get error %d", error); + goto Done; + } + } + + switch (size) { + case I2C_SMBUS_BYTE_DATA: + cnt = 1; break; + case I2C_SMBUS_WORD_DATA: + cnt = 2; break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: + /* In block data modes keep number of byte in block[0] */ + cnt = data->block[0]; + break; + default: + cnt = 0; break; + } + + // [CNT] used only block data write + if (size == I2C_SMBUS_BLOCK_DATA && rw == I2C_SMBUS_WRITE) { + + iowrite8(cnt, pci_bar + REG_DR0); + info( "MS Send CNT 0x%2.2X", cnt); + + // Wait {A} + error = i2c_wait_ack(adapter, 12, 1); + if (error < 0) { + info( "get error %d", error); + goto Done; + } + } + + // [DATA]{A} + if ( rw == I2C_SMBUS_WRITE && ( + size == I2C_SMBUS_BYTE || + size == I2C_SMBUS_BYTE_DATA || + size == I2C_SMBUS_WORD_DATA || + size == I2C_SMBUS_BLOCK_DATA || + size == I2C_SMBUS_I2C_BLOCK_DATA + )) { + bid = 0; + info( "MS prepare to sent [%d bytes]", cnt); + if (size == I2C_SMBUS_BLOCK_DATA || size == I2C_SMBUS_I2C_BLOCK_DATA) { + bid = 1; // block[0] is cnt; + cnt += 1; // offset from block[0] + } + for (; bid < cnt; bid++) { + + iowrite8(data->block[bid], pci_bar + REG_DR0); + info( " Data > %2.2X", data->block[bid]); + // Wait {A} + error = i2c_wait_ack(adapter, 12, 1); + if (error < 0) { + goto Done; + } + } + + } + + // REPEATE START + if ( rw == I2C_SMBUS_READ && ( + size == I2C_SMBUS_BYTE_DATA || + size == I2C_SMBUS_WORD_DATA || + size == I2C_SMBUS_BLOCK_DATA || + size == I2C_SMBUS_I2C_BLOCK_DATA + )) { + info( "MS Repeated Start"); + + SET_REG_BIT_L(pci_bar + REG_CR0, I2C_CR_BIT_MEN); + iowrite8(1 << I2C_CR_BIT_MIEN | + 1 << I2C_CR_BIT_MTX | + 1 << I2C_CR_BIT_MSTA | + 1 << I2C_CR_BIT_RSTA , pci_bar + REG_CR0); + SET_REG_BIT_H(pci_bar + REG_CR0, I2C_CR_BIT_MEN); + + // sent Address with Read mode + iowrite8( addr << 1 | 0x1 , pci_bar + REG_DR0); + + // Wait {A} + error = i2c_wait_ack(adapter, 12, 1); + if (error < 0) { + goto Done; + } + + } + + if ( rw == I2C_SMBUS_READ && ( + size == I2C_SMBUS_BYTE || + size == I2C_SMBUS_BYTE_DATA || + size == I2C_SMBUS_WORD_DATA || + size == I2C_SMBUS_BLOCK_DATA || + size == I2C_SMBUS_I2C_BLOCK_DATA + )) { + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + cnt = 1; break; + case I2C_SMBUS_WORD_DATA: + cnt = 2; break; + case I2C_SMBUS_BLOCK_DATA: + // will be changed after recived first data + cnt = 3; break; + case I2C_SMBUS_I2C_BLOCK_DATA: + cnt = data->block[0]; break; + default: + cnt = 0; break; + } + + bid = 0; + info( "MS Receive"); + + //set to Receive mode + iowrite8(1 << I2C_CR_BIT_MEN | + 1 << I2C_CR_BIT_MIEN | + 1 << I2C_CR_BIT_MSTA , pci_bar + REG_CR0); + + for (bid = -1; bid < cnt; bid++) { + + // Wait for byte transfer + error = i2c_wait_ack(adapter, 12, 0); + if (error < 0) { + goto Done; + } + + if (bid == cnt - 2) { + info( "SET NAK"); + SET_REG_BIT_H(pci_bar + REG_CR0, I2C_CR_BIT_TXAK); + } + + if (bid < 0) { + ioread8(pci_bar + REG_DR0); + info( "READ Dummy Byte" ); + } else { + + if (bid == cnt - 1) { + info ( "SET STOP in read loop"); + SET_REG_BIT_L(pci_bar + REG_CR0, I2C_CR_BIT_MSTA); + } + if (size == I2C_SMBUS_I2C_BLOCK_DATA) { + // block[0] is read length + data->block[bid + 1] = ioread8(pci_bar + REG_DR0); + } else { + data->block[bid] = ioread8(pci_bar + REG_DR0); + } + info( "DATA IN [%d] %2.2X", bid, data->block[bid]); + + if (size == I2C_SMBUS_BLOCK_DATA && bid == 0) { + cnt = data->block[0] + 1; + } + } + } + } + + // [P] + SET_REG_BIT_L(pci_bar + REG_CR0, I2C_CR_BIT_MSTA); + info( "MS STOP"); + +Done: + iowrite8(1 << I2C_CR_BIT_MEN, pci_bar + REG_CR0); + check(pci_bar + REG_CR0); + check(pci_bar + REG_SR0); +#ifdef DEBUG_KERN + printk(KERN_INFO "END --- Error code %d", error); +#endif + + return error; +} + +/** + * Wrapper of smbus_access access with PCA9548 I2C switch management. + * This function set PCA9548 switches to the proper slave channel. + * Only one channel among switches chip is selected during communication time. + * + * Note: If the bus does not have any PCA9548 on it, the switch_addr must be + * set to 0xFF, it will use normal smbus_access function. + */ +static int fpga_i2c_access(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data) +{ + int error = 0; + struct i2c_dev_data *dev_data; + unsigned char master_bus; + unsigned char switch_addr; + unsigned char channel; + uint16_t prev_port = 0; + unsigned char prev_switch; + unsigned char prev_ch; + int retry; + + dev_data = i2c_get_adapdata(adapter); + master_bus = dev_data->pca9548.master_bus; + switch_addr = dev_data->pca9548.switch_addr; + channel = dev_data->pca9548.channel; + + // Acquire the master resource. + mutex_lock(&fpga_i2c_master_locks[master_bus - 1]); + prev_port = fpga_i2c_lasted_access_port[master_bus - 1]; + prev_switch = (unsigned char)(prev_port >> 8) & 0xFF; + prev_ch = (unsigned char)(prev_port & 0xFF); + + if (switch_addr != 0xFF) { + + // Check lasted access switch address on a master + if ( prev_switch != switch_addr && prev_switch != 0 ) { + // reset prev_port PCA9548 chip + retry = 3; + while (retry--) { + error = smbus_access(adapter, (u16)(prev_switch), flags, + I2C_SMBUS_WRITE, 0x00, + I2C_SMBUS_BYTE, NULL); + if (error >= 0) { + break; + } else { + dev_dbg(&adapter->dev, + "Failed to deselect ch %d of 0x%x, CODE %d\n", + prev_ch, prev_switch, error); + } + } + if (retry < 0) { + goto release_unlock; + } + // set PCA9548 to current channel + retry = 3; + while (retry--) { + error = smbus_access(adapter, switch_addr, flags, + I2C_SMBUS_WRITE, 1 << channel, + I2C_SMBUS_BYTE, NULL); + if (error >= 0) { + break; + } else { + dev_dbg(&adapter->dev, + "Failed to deselect ch %d of 0x%x, CODE %d\n", + prev_ch, prev_switch, error); + } + } + if (retry < 0) { + goto release_unlock; + } + // update lasted port + fpga_i2c_lasted_access_port[master_bus - 1] = switch_addr << 8 | channel; + + } else { + // check if channel is also changes + if ( prev_ch != channel || prev_switch == 0 ) { + // set new PCA9548 at switch_addr to current + retry = 3; + while (retry--) { + error = smbus_access(adapter, switch_addr, flags, + I2C_SMBUS_WRITE, 1 << channel, + I2C_SMBUS_BYTE, NULL); + if (error >= 0) { + break; + } else { + dev_dbg(&adapter->dev, + "Failed to deselect ch %d of 0x%x, CODE %d\n", + prev_ch, prev_switch, error); + } + } + if (retry < 0) { + goto release_unlock; + } + // update lasted port + fpga_i2c_lasted_access_port[master_bus - 1] = switch_addr << 8 | channel; + } + } + } + + // Do SMBus communication + error = smbus_access(adapter, addr, flags, rw, cmd, size, data); + if (error < 0) { + dev_dbg( &adapter->dev, + "smbus_xfer failed (%d) @ 0x%2.2X|f 0x%4.4X|(%d)%-5s| (%d)%-10s|CMD %2.2X " + , error, addr, flags, rw, rw == 1 ? "READ " : "WRITE" + , size, size == 0 ? "QUICK" : + size == 1 ? "BYTE" : + size == 2 ? "BYTE_DATA" : + size == 3 ? "WORD_DATA" : + size == 4 ? "PROC_CALL" : + size == 5 ? "BLOCK_DATA" : + size == 8 ? "I2C_BLOCK_DATA" : "ERROR" + , cmd); + } + +release_unlock: + mutex_unlock(&fpga_i2c_master_locks[master_bus - 1]); + dev_dbg(&adapter->dev, "switch ch %d of 0x%x -> ch %d of 0x%x\n", + prev_ch, prev_switch, channel, switch_addr); + return error; +} + + + +/** + * A callback function show available smbus functions. + */ +static u32 fpga_i2c_func(struct i2c_adapter *a) +{ + return I2C_FUNC_SMBUS_QUICK | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm seastone2_i2c_algorithm = { + .smbus_xfer = fpga_i2c_access, + .functionality = fpga_i2c_func, +}; + +/** + * Create virtual I2C bus adapter for switch devices + * @param pdev platform device pointer + * @param portid virtual i2c port id for switch device mapping + * @param bus_number_offset bus offset for virtual i2c adapter in system + * @return i2c adapter. + * + * When bus_number_offset is -1, created adapter with dynamic bus number. + * Otherwise create adapter at i2c bus = bus_number_offset + portid. + */ +static struct i2c_adapter * seastone2_i2c_init(struct platform_device *pdev, + int portid, int bus_number_offset) +{ + int error; + struct i2c_adapter *new_adapter; + struct i2c_dev_data *new_data; + void __iomem *i2c_freq_base_reg; + + new_adapter = kzalloc(sizeof(*new_adapter), GFP_KERNEL); + if (!new_adapter) { + printk(KERN_ALERT "Cannot alloc i2c adapter for %s", + fpga_i2c_bus_dev[portid].calling_name); + return NULL; + } + + new_adapter->owner = THIS_MODULE; + new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + new_adapter->algo = &seastone2_i2c_algorithm; + /* If the bus offset is -1, use dynamic bus number */ + if (bus_number_offset == -1) { + new_adapter->nr = -1; + } else { + new_adapter->nr = bus_number_offset + portid; + } + + new_data = kzalloc(sizeof(*new_data), GFP_KERNEL); + if (!new_data) { + printk(KERN_ALERT "Cannot alloc i2c data for %s", + fpga_i2c_bus_dev[portid].calling_name); + kzfree(new_adapter); + return NULL; + } + + new_data->portid = portid; + new_data->pca9548.master_bus = fpga_i2c_bus_dev[portid].master_bus; + new_data->pca9548.switch_addr = fpga_i2c_bus_dev[portid].switch_addr; + new_data->pca9548.channel = fpga_i2c_bus_dev[portid].channel; + strcpy(new_data->pca9548.calling_name, fpga_i2c_bus_dev[portid].calling_name); + + snprintf(new_adapter->name, sizeof(new_adapter->name), + "SMBus I2C Adapter PortID: %s", new_data->pca9548.calling_name); + + i2c_freq_base_reg = fpga_dev.data_base_addr + I2C_MASTER_FREQ_1; + iowrite8(0x07, i2c_freq_base_reg + (new_data->pca9548.master_bus - 1) * 0x100); // 0x07 400kHz + i2c_set_adapdata(new_adapter, new_data); + error = i2c_add_numbered_adapter(new_adapter); + if (error < 0) { + printk(KERN_ALERT "Cannot add i2c adapter %s", new_data->pca9548.calling_name); + kzfree(new_adapter); + kzfree(new_data); + return NULL; + } + + return new_adapter; +}; + +/** + * Board info for QSFP/SFP+ eeprom. + * Note: Using sff8436 as I2C eeprom driver. + */ +static struct i2c_board_info sff8436_eeprom_info[] = { + { I2C_BOARD_INFO("optoe1", 0x50) }, + { I2C_BOARD_INFO("optoe2", 0x50) }, +}; + +static int seastone2_drv_probe(struct platform_device *pdev) +{ + int ret = 0; + int portid_count; + uint8_t cpld1_version, cpld2_version; + uint16_t prev_i2c_switch = 0; + struct sff_device_data *sff_data; + + /* The device class need to be instantiated before this function called */ + BUG_ON(fpgafwclass == NULL); + + fpga_data = devm_kzalloc(&pdev->dev, sizeof(struct seastone2_fpga_data), + GFP_KERNEL); + + if (!fpga_data) + return -ENOMEM; + + // Set default read address to VERSION + fpga_data->fpga_read_addr = fpga_dev.data_base_addr + FPGA_VERSION; + fpga_data->cpld1_read_addr = 0x00; + fpga_data->cpld2_read_addr = 0x00; + + mutex_init(&fpga_data->fpga_lock); + + for (ret = I2C_MASTER_CH_1 ; ret <= I2C_MASTER_CH_TOTAL; ret++) { + mutex_init(&fpga_i2c_master_locks[ret - 1]); + } + + fpga = kobject_create_and_add("FPGA", &pdev->dev.kobj); + if (!fpga) { + kzfree(fpga_data); + return -ENOMEM; + } + + ret = sysfs_create_group(fpga, &fpga_attr_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create FPGA sysfs attributes\n"); + kobject_put(fpga); + kzfree(fpga_data); + return ret; + } + + cpld1 = kobject_create_and_add("CPLD1", &pdev->dev.kobj); + if (!cpld1) { + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return -ENOMEM; + } + ret = sysfs_create_group(cpld1, &cpld1_attr_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create CPLD1 sysfs attributes\n"); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return ret; + } + + cpld2 = kobject_create_and_add("CPLD2", &pdev->dev.kobj); + if (!cpld2) { + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return -ENOMEM; + } + ret = sysfs_create_group(cpld2, &cpld2_attr_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create CPLD2 sysfs attributes\n"); + kobject_put(cpld2); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return ret; + } + + sff_dev = device_create(fpgafwclass, NULL, MKDEV(0, 0), NULL, "sff_device"); + if (IS_ERR(sff_dev)) { + printk(KERN_ERR "Failed to create sff device\n"); + sysfs_remove_group(cpld2, &cpld2_attr_grp); + kobject_put(cpld2); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return PTR_ERR(sff_dev); + } + + ret = sysfs_create_group(&sff_dev->kobj, &sff_led_test_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create SFF attributes\n"); + device_destroy(fpgafwclass, MKDEV(0, 0)); + sysfs_remove_group(cpld2, &cpld2_attr_grp); + kobject_put(cpld2); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return ret; + } + + ret = sysfs_create_link(&pdev->dev.kobj, &sff_dev->kobj, "SFF"); + if (ret != 0) { + sysfs_remove_group(&sff_dev->kobj, &sff_led_test_grp); + device_destroy(fpgafwclass, MKDEV(0, 0)); + sysfs_remove_group(cpld2, &cpld2_attr_grp); + kobject_put(cpld2); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return ret; + } + + for (portid_count = 0 ; portid_count < VIRTUAL_I2C_PORT_LENGTH ; portid_count++) { + fpga_data->i2c_adapter[portid_count] = seastone2_i2c_init(pdev, portid_count, VIRTUAL_I2C_BUS_OFFSET); + } + + + /* Init SFF devices */ + for (portid_count = 0; portid_count < SFF_PORT_TOTAL; portid_count++) { + struct i2c_adapter *i2c_adap = fpga_data->i2c_adapter[portid_count]; + if (i2c_adap) { + fpga_data->sff_devices[portid_count] = seastone2_sff_init(portid_count); + sff_data = dev_get_drvdata(fpga_data->sff_devices[portid_count]); + BUG_ON(sff_data == NULL); + if ( sff_data->port_type == QSFP ) { + fpga_data->sff_i2c_clients[portid_count] = i2c_new_device(i2c_adap, &sff8436_eeprom_info[0]); + } else { + fpga_data->sff_i2c_clients[portid_count] = i2c_new_device(i2c_adap, &sff8436_eeprom_info[1]); + } + sff_data = NULL; + sysfs_create_link(&fpga_data->sff_devices[portid_count]->kobj, + &fpga_data->sff_i2c_clients[portid_count]->dev.kobj, + "i2c"); + } + + } + + printk(KERN_INFO "Virtual I2C buses created\n"); + +#ifdef TEST_MODE + return 0; +#endif + fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], CPLD1_SLAVE_ADDR, 0x00, + I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&cpld1_version); + fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], CPLD2_SLAVE_ADDR, 0x00, + I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&cpld2_version); + + printk(KERN_INFO "CPLD1 VERSON: %2.2x\n", cpld1_version); + printk(KERN_INFO "CPLD2 VERSON: %2.2x\n", cpld2_version); + + /* Init I2C buses that has PCA9548 switch device. */ + for (portid_count = 0; portid_count < VIRTUAL_I2C_PORT_LENGTH; portid_count++) { + + struct i2c_dev_data *dev_data; + unsigned char master_bus; + unsigned char switch_addr; + + dev_data = i2c_get_adapdata(fpga_data->i2c_adapter[portid_count]); + master_bus = dev_data->pca9548.master_bus; + switch_addr = dev_data->pca9548.switch_addr; + + if (switch_addr != 0xFF) { + + if (prev_i2c_switch != ( (master_bus << 8) | switch_addr) ) { + // Found the bus with PCA9548, trying to clear all switch in it. + smbus_access(fpga_data->i2c_adapter[portid_count], switch_addr, 0x00, + I2C_SMBUS_WRITE, 0x00, I2C_SMBUS_BYTE, NULL); + prev_i2c_switch = ( master_bus << 8 ) | switch_addr; + } + } + } + return 0; +} + +static int seastone2_drv_remove(struct platform_device *pdev) +{ + int portid_count; + struct sff_device_data *rem_data; + + for (portid_count = 0; portid_count < SFF_PORT_TOTAL; portid_count++) { + sysfs_remove_link(&fpga_data->sff_devices[portid_count]->kobj, "i2c"); + i2c_unregister_device(fpga_data->sff_i2c_clients[portid_count]); + } + + for (portid_count = 0 ; portid_count < VIRTUAL_I2C_PORT_LENGTH ; portid_count++) { + if (fpga_data->i2c_adapter[portid_count] != NULL) { + info(KERN_INFO "<%x>", fpga_data->i2c_adapter[portid_count]); + i2c_del_adapter(fpga_data->i2c_adapter[portid_count]); + } + } + + for (portid_count = 0; portid_count < SFF_PORT_TOTAL; portid_count++) { + if (fpga_data->sff_devices[portid_count] != NULL) { + rem_data = dev_get_drvdata(fpga_data->sff_devices[portid_count]); + device_unregister(fpga_data->sff_devices[portid_count]); + put_device(fpga_data->sff_devices[portid_count]); + kfree(rem_data); + } + } + + sysfs_remove_group(fpga, &fpga_attr_grp); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + sysfs_remove_group(cpld2, &cpld2_attr_grp); + sysfs_remove_group(&sff_dev->kobj, &sff_led_test_grp); + kobject_put(fpga); + kobject_put(cpld1); + kobject_put(cpld2); + device_destroy(fpgafwclass, MKDEV(0, 0)); + devm_kfree(&pdev->dev, fpga_data); + return 0; +} + +static struct platform_driver seastone2_drv = { + .probe = seastone2_drv_probe, + .remove = __exit_p(seastone2_drv_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +#ifdef TEST_MODE +#define FPGA_PCI_BAR_NUM 2 +#else +#define FPGA_PCI_BAR_NUM 0 +#endif + +static const struct pci_device_id fpga_id_table[] = { + { PCI_VDEVICE(XILINX, FPGA_PCIE_DEVICE_ID) }, + { PCI_VDEVICE(TEST, TEST_PCIE_DEVICE_ID) }, + {0, } +}; + +MODULE_DEVICE_TABLE(pci, fpga_id_table); + +static int fpga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int err; + struct device *dev = &pdev->dev; + uint32_t fpga_version; + + if ((err = pci_enable_device(pdev))) { + dev_err(dev, "pci_enable_device probe error %d for device %s\n", + err, pci_name(pdev)); + return err; + } + + if ((err = pci_request_regions(pdev, FPGA_PCI_NAME)) < 0) { + dev_err(dev, "pci_request_regions error %d\n", err); + goto pci_disable; + } + + /* bar0: data mmio region */ + fpga_dev.data_mmio_start = pci_resource_start(pdev, FPGA_PCI_BAR_NUM); + fpga_dev.data_mmio_len = pci_resource_len(pdev, FPGA_PCI_BAR_NUM); + fpga_dev.data_base_addr = pci_iomap(pdev, FPGA_PCI_BAR_NUM, 0); + if (!fpga_dev.data_base_addr) { + dev_err(dev, "cannot iomap region of size %lu\n", + (unsigned long)fpga_dev.data_mmio_len); + goto pci_release; + } + dev_info(dev, "data_mmio iomap base = 0x%lx \n", + (unsigned long)fpga_dev.data_base_addr); + dev_info(dev, "data_mmio_start = 0x%lx data_mmio_len = %lu\n", + (unsigned long)fpga_dev.data_mmio_start, + (unsigned long)fpga_dev.data_mmio_len); + + printk(KERN_INFO "FPGA PCIe driver probe OK.\n"); + printk(KERN_INFO "FPGA ioremap registers of size %lu\n", (unsigned long)fpga_dev.data_mmio_len); + printk(KERN_INFO "FPGA Virtual BAR %d at %8.8lx - %8.8lx\n", FPGA_PCI_BAR_NUM, + (unsigned long)fpga_dev.data_base_addr, + (unsigned long)(fpga_dev.data_base_addr + fpga_dev.data_mmio_len)); + printk(KERN_INFO ""); + fpga_version = ioread32(fpga_dev.data_base_addr); + printk(KERN_INFO "FPGA VERSION : %8.8x\n", fpga_version); + if ((err = fpgafw_init()) < 0) { + goto pci_unmap; + } + seastone2_dev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); + platform_driver_register(&seastone2_drv); + return 0; + +pci_unmap: + pci_iounmap(pdev, fpga_dev.data_base_addr); +pci_release: + pci_release_regions(pdev); +pci_disable: + pci_disable_device(pdev); + return -EBUSY; +} + +static void fpga_pci_remove(struct pci_dev *pdev) +{ + platform_driver_unregister(&seastone2_drv); + platform_device_unregister(seastone2_dev); + fpgafw_exit(); + pci_iounmap(pdev, fpga_dev.data_base_addr); + pci_release_regions(pdev); + pci_disable_device(pdev); + printk(KERN_INFO "FPGA PCIe driver remove OK.\n"); +}; + +static struct pci_driver pci_dev_ops = { + .name = FPGA_PCI_NAME, + .probe = fpga_pci_probe, + .remove = fpga_pci_remove, + .id_table = fpga_id_table, +}; + +enum { + READREG, + WRITEREG +}; + +struct fpga_reg_data { + uint32_t addr; + uint32_t value; +}; + +static long fpgafw_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { + int ret = 0; + struct fpga_reg_data data; + mutex_lock(&fpga_data->fpga_lock); + +#ifdef TEST_MODE + static uint32_t status_reg; +#endif + // Switch function to read and write. + switch (cmd) { + case READREG: + if (copy_from_user(&data, (void __user*)arg, sizeof(data)) != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EFAULT; + } + data.value = ioread32(fpga_dev.data_base_addr + data.addr); + if (copy_to_user((void __user*)arg , &data, sizeof(data)) != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EFAULT; + } +#ifdef TEST_MODE + if (data.addr == 0x1210) { + switch (status_reg) { + case 0x0000 : status_reg = 0x8000; + break; + + case 0x8080 : status_reg = 0x80C0; + break; + case 0x80C0 : status_reg = 0x80F0; + break; + case 0x80F0 : status_reg = 0x80F8; + break; + + } + iowrite32(status_reg, fpga_dev.data_base_addr + 0x1210); + } +#endif + + + break; + case WRITEREG: + if (copy_from_user(&data, (void __user*)arg, sizeof(data)) != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EFAULT; + } + iowrite32(data.value, fpga_dev.data_base_addr + data.addr); + +#ifdef TEST_MODE + if (data.addr == 0x1204) { + status_reg = 0x8080; + iowrite32(status_reg, fpga_dev.data_base_addr + 0x1210); + } +#endif + + break; + default: + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + mutex_unlock(&fpga_data->fpga_lock); + return ret; +} + + +const struct file_operations fpgafw_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = fpgafw_unlocked_ioctl, +}; + + +static int fpgafw_init(void) { + printk(KERN_INFO "Initializing the switchboard driver\n"); + // Try to dynamically allocate a major number for the device -- more difficult but worth it + majorNumber = register_chrdev(0, DEVICE_NAME, &fpgafw_fops); + if (majorNumber < 0) { + printk(KERN_ALERT "Failed to register a major number\n"); + return majorNumber; + } + printk(KERN_INFO "Device registered correctly with major number %d\n", majorNumber); + + // Register the device class + fpgafwclass = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(fpgafwclass)) { // Check for error and clean up if there is + unregister_chrdev(majorNumber, DEVICE_NAME); + printk(KERN_ALERT "Failed to register device class\n"); + return PTR_ERR(fpgafwclass); + } + printk(KERN_INFO "Device class registered correctly\n"); + + // Register the device driver + fpgafwdev = device_create(fpgafwclass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME); + if (IS_ERR(fpgafwdev)) { // Clean up if there is an error + class_destroy(fpgafwclass); // Repeated code but the alternative is goto statements + unregister_chrdev(majorNumber, DEVICE_NAME); + printk(KERN_ALERT "Failed to create the FW upgrade device node\n"); + return PTR_ERR(fpgafwdev); + } + printk(KERN_INFO "FPGA fw upgrade device node created correctly\n"); // Made it! device was initialized + return 0; +} + +static void fpgafw_exit(void) { + device_destroy(fpgafwclass, MKDEV(majorNumber, 0)); // remove the device + class_destroy(fpgafwclass); // remove the device class + unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number + printk(KERN_INFO "Goodbye!\n"); +} + +int seastone2_init(void) +{ + int rc; + rc = pci_register_driver(&pci_dev_ops); + if (rc) + return rc; + return 0; +} + +void seastone2_exit(void) +{ + pci_unregister_driver(&pci_dev_ops); +} + +module_init(seastone2_init); +module_exit(seastone2_exit); + +MODULE_AUTHOR("Pradchaya P. "); +MODULE_DESCRIPTION("Celestica Seastone2 switchboard driver"); +MODULE_VERSION(MOD_VERSION); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/seastone2/systemd/platform-modules-seastone2.service b/platform/broadcom/sonic-platform-modules-cel/seastone2/systemd/platform-modules-seastone2.service new file mode 100644 index 000000000000..02553bf24538 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/seastone2/systemd/platform-modules-seastone2.service @@ -0,0 +1,14 @@ + +[Unit] +Description=Celestica seastone2 platform modules +After=local-fs.target +Before=pmon.service + +[Service] +Type=oneshot +ExecStart=-/etc/init.d/platform-modules-seastone2 start +ExecStop=-/etc/init.d/platform-modules-seastone2 stop +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target \ No newline at end of file From 97099262689408dac45ee34140eac1fb12dfe25d Mon Sep 17 00:00:00 2001 From: Kantaphon Kovathana Date: Thu, 18 Mar 2021 16:34:48 +0700 Subject: [PATCH 3/4] [platform/cel] add sonic_platform in services --- .../platform_api/platform_api_mgnt.sh | 1 + .../services/platform_api/setup.py | 30 + .../platform_api/sonic_platform/__init__.py | 2 + .../platform_api/sonic_platform/chassis.py | 317 +++ .../platform_api/sonic_platform/common.py | 283 +++ .../platform_api/sonic_platform/component.py | 57 + .../platform_api/sonic_platform/eeprom.py | 113 + .../platform_api/sonic_platform/fan.py | 193 ++ .../platform_api/sonic_platform/fan_drawer.py | 82 + .../platform_api/sonic_platform/platform.py | 23 + .../platform_api/sonic_platform/psu.py | 182 ++ .../platform_api/sonic_platform/sfp.py | 2037 +++++++++++++++++ .../platform_api/sonic_platform/thermal.py | 176 ++ 13 files changed, 3496 insertions(+) create mode 100755 platform/broadcom/sonic-platform-modules-cel/services/platform_api/setup.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/__init__.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/chassis.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/common.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/component.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/eeprom.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/fan.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/fan_drawer.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/platform.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/psu.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/sfp.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/thermal.py diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/platform_api_mgnt.sh b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/platform_api_mgnt.sh index e1d330357894..493119bd37f6 100755 --- a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/platform_api_mgnt.sh +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/platform_api_mgnt.sh @@ -9,6 +9,7 @@ install() { # Install sonic-platform package if [ -e $DEVICE/$PLATFORM/sonic_platform-1.0-py2-none-any.whl ]; then pip install $DEVICE/$PLATFORM/sonic_platform-1.0-py2-none-any.whl + pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl fi } diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/setup.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/setup.py new file mode 100755 index 000000000000..b0c8d1d8c9b8 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/setup.py @@ -0,0 +1,30 @@ +from setuptools import setup + +setup( + name='sonic-platform', + version='1.0', + description='SONiC platform API implementation on Celestica Platforms', + license='Apache 2.0', + author='SONiC Team', + author_email='linuxnetdev@microsoft.com', + url='https://github.com/Azure/sonic-buildimage', + maintainer='Wirut Getbamrung', + maintainer_email='wgetbumr@celestica.com', + packages=[ + 'sonic_platform', + ], + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: Plugins', + 'Intended Audience :: Developers', + 'Intended Audience :: Information Technology', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Natural Language :: English', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python :: 2.7', + 'Topic :: Utilities', + ], + keywords='sonic SONiC platform PLATFORM', +) + diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/__init__.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/__init__.py new file mode 100644 index 000000000000..7b86fa12b515 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/__init__.py @@ -0,0 +1,2 @@ +import chassis +import platform diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/chassis.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/chassis.py new file mode 100644 index 000000000000..aa7671484889 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/chassis.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Module contains an implementation of SONiC Platform Base API and +# provides the Chassis information which are available in the platform +# +############################################################################# + +try: + import sys + from sonic_platform_base.chassis_base import ChassisBase + from common import Common +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Chassis(ChassisBase): + """Platform-specific Chassis class""" + + CHASSIS_CONFIG = 'chassis.json' + THERMAL_CONFIG = 'thermal.json' + SFP_CONFIG = 'sfp.json' + PSU_CONFIG = 'psu.json' + FAN_CONFIG = 'fan.json' + COMPONENT_CONFIG = 'component.json' + + def __init__(self): + ChassisBase.__init__(self) + self._api_common = Common() + config_path = self._api_common.get_config_path(self.CHASSIS_CONFIG) + self._config = self._api_common.load_json_file(config_path) + + self.sfp_module_initialized = False + self.__initialize_eeprom() + + if not self._api_common.is_host(): + self.__initialize_fan() + self.__initialize_psu() + self.__initialize_thermals() + else: + self.__initialize_components() + + def __initialize_fan(self): + from sonic_platform.fan import Fan + from sonic_platform.fan_drawer import FanDrawer + + fan_config_path = self._api_common.get_config_path(self.FAN_CONFIG) + self._fan_config = self._api_common.load_json_file(fan_config_path) + + if self._fan_config: + fan_index = 0 + for drawer_index in range(0, self._fan_config['drawer_num']): + drawer_fan_list = [] + for index in range(0, self._fan_config['fan_num_per_drawer']): + fan = Fan(fan_index, conf=self._fan_config) + fan_index += 1 + self._fan_list.append(fan) + drawer_fan_list.append(fan) + fan_drawer = FanDrawer(drawer_index, drawer_fan_list) + self._fan_drawer_list.append(fan_drawer) + + def __initialize_sfp(self): + from sonic_platform.sfp import Sfp + + sfp_config_path = self._api_common.get_config_path(self.SFP_CONFIG) + sfp_config = self._api_common.load_json_file(sfp_config_path) + + sfp_index = 0 + for index in range(0, sfp_config['port_num']): + sfp = Sfp(sfp_index, conf=sfp_config) + self._sfp_list.append(sfp) + sfp_index += 1 + self.sfp_module_initialized = True + + def __initialize_psu(self): + from sonic_platform.psu import Psu + + psu_config_path = self._api_common.get_config_path(self.PSU_CONFIG) + psu_config = self._api_common.load_json_file(psu_config_path) + + if psu_config: + psu_index = 0 + for index in range(0, psu_config['psu_num']): + psu = Psu(psu_index, conf=psu_config, + fan_conf=self._fan_config) + psu_index += 1 + self._psu_list.append(psu) + + def __initialize_thermals(self): + from sonic_platform.thermal import Thermal + + thermal_config_path = self._api_common.get_config_path( + self.THERMAL_CONFIG) + thermal_config = self._api_common.load_json_file(thermal_config_path) + + thermal_index = 0 + for index in range(0, thermal_config['thermal_num']): + thermal = Thermal(thermal_index, conf=thermal_config) + thermal_index += 1 + self._thermal_list.append(thermal) + + def __initialize_eeprom(self): + from sonic_platform.eeprom import Tlv + self._eeprom = Tlv(self._config) + + def __initialize_components(self): + from component import Component + + component_config_path = self._api_common.get_config_path( + self.COMPONENT_CONFIG) + component_config = self._api_common.load_json_file( + component_config_path) + + for index in range(0, component_config['component_num']): + component = Component(index, conf=component_config) + self._component_list.append(component) + + def get_base_mac(self): + """ + Retrieves the base MAC address for the chassis + Returns: + A string containing the MAC address in the format + 'XX:XX:XX:XX:XX:XX' + """ + return self._eeprom.get_mac() + + def get_system_eeprom_info(self): + """ + Retrieves the full content of system EEPROM information for the chassis + Returns: + A dictionary where keys are the type code defined in + OCP ONIE TlvInfo EEPROM format and values are their corresponding + values. + """ + return self._eeprom.get_eeprom() + + def get_reboot_cause(self): + """ + Retrieves the cause of the previous reboot + Returns: + A tuple (string, string) where the first element is a string + containing the cause of the previous reboot. This string must be + one of the predefined strings in this class. If the first string + is "REBOOT_CAUSE_HARDWARE_OTHER", the second string can be used + to pass a description of the reboot cause. + + Avaliable reboot cause: + REBOOT_CAUSE_POWER_LOSS = "Power Loss" + REBOOT_CAUSE_THERMAL_OVERLOAD_CPU = "Thermal Overload: CPU" + REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC = "Thermal Overload: ASIC" + REBOOT_CAUSE_THERMAL_OVERLOAD_OTHER = "Thermal Overload: Other" + REBOOT_CAUSE_INSUFFICIENT_FAN_SPEED = "Insufficient Fan Speed" + REBOOT_CAUSE_WATCHDOG = "Watchdog" + REBOOT_CAUSE_HARDWARE_OTHER = "Hardware - Other" + REBOOT_CAUSE_NON_HARDWARE = "Non-Hardware" + + """ + reboot_cause = self._api_common.get_output( + 0, self._config['get_reboot_cause'], self.REBOOT_CAUSE_HARDWARE_OTHER) + description = self._api_common.get_output( + 0, self._config['get_reboot_description'], 'Unknown') + + return (reboot_cause, description) + + # ############################################################## + # ######################## SFP methods ######################### + # ############################################################## + + def get_num_sfps(self): + """ + Retrieves the number of sfps available on this chassis + Returns: + An integer, the number of sfps available on this chassis + """ + if not self.sfp_module_initialized: + self.__initialize_sfp() + + return len(self._sfp_list) + + def get_all_sfps(self): + """ + Retrieves all sfps available on this chassis + Returns: + A list of objects derived from SfpBase representing all sfps + available on this chassis + """ + if not self.sfp_module_initialized: + self.__initialize_sfp() + + return self._sfp_list + + def get_sfp(self, index): + """ + Retrieves sfp represented by (1-based) index + Args: + index: An integer, the index (1-based) of the sfp to retrieve. + The index should be the sequence of a physical port in a chassis, + starting from 1. + For example, 1 for Ethernet0, 2 for Ethernet4 and so on. + Returns: + An object dervied from SfpBase representing the specified sfp + """ + sfp = None + if not self.sfp_module_initialized: + self.__initialize_sfp() + + try: + # The index will start from 1 + sfp = self._sfp_list[index-1] + except IndexError: + sys.stderr.write("SFP index {} out of range (1-{})\n".format( + index, len(self._sfp_list))) + return sfp + + ############################################################## + ###################### Event methods ######################### + ############################################################## + + def get_change_event(self, timeout=0): + """ + Returns a nested dictionary containing all devices which have + experienced a change at chassis level + Args: + timeout: Timeout in milliseconds (optional). If timeout == 0, + this method will block until a change is detected. + Returns: + (bool, dict): + - True if call successful, False if not; + - A nested dictionary where key is a device type, + value is a dictionary with key:value pairs in the format of + {'device_id':'device_event'}, + where device_id is the device ID for this device and + device_event, + status='1' represents device inserted, + status='0' represents device removed. + Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0'}} + indicates that fan 0 has been removed, fan 2 + has been inserted and sfp 11 has been removed. + Specifically for SFP event, besides SFP plug in and plug out, + there are some other error event could be raised from SFP, when + these error happened, SFP eeprom will not be avalaible, XCVRD shall + stop to read eeprom before SFP recovered from error status. + status='2' I2C bus stuck, + status='3' Bad eeprom, + status='4' Unsupported cable, + status='5' High Temperature, + status='6' Bad cable. + """ + + if not self.sfp_module_initialized: + self.__initialize_sfp() + + return self._api_common.get_event(timeout, self._config['get_change_event'], self._sfp_list) + + # ############################################################## + # ###################### Other methods ######################## + # ############################################################## + + def get_watchdog(self): + """ + Retreives hardware watchdog device on this chassis + Returns: + An object derived from WatchdogBase representing the hardware + watchdog device + """ + if self._watchdog is None: + wdt = self._api_common.get_output( + 0, self._config['get_watchdog'], None) + self._watchdog = wdt() + + return self._watchdog + + # ############################################################## + # ###################### Device methods ######################## + # ############################################################## + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + return self._api_common.hwsku + + def get_presence(self): + """ + Retrieves the presence of the PSU + Returns: + bool: True if PSU is present, False if not + """ + return True + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + return self._eeprom.get_pn() + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return self._eeprom.get_serial() + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return True diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/common.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/common.py new file mode 100644 index 000000000000..697308b8c3cf --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/common.py @@ -0,0 +1,283 @@ +import os +import imp +import yaml +import subprocess + +from sonic_py_common import device_info + + +class Common: + + DEVICE_PATH = '/usr/share/sonic/device/' + PMON_PLATFORM_PATH = '/usr/share/sonic/platform/' + CONFIG_DIR = 'sonic_platform_config' + + OUTPUT_SOURCE_IPMI = 'ipmitool' + OUTPUT_SOURCE_GIVEN_LIST = 'value_list' + OUTPUT_SOURCE_GIVEN_VALUE = 'value' + OUTPUT_SOURCE_GIVEN_CLASS = 'class' + OUTPUT_SOURCE_SYSFS = 'sysfs_value' + OUTPUT_SOURCE_FUNC = 'function' + OUTPUT_SOURCE_GIVEN_TXT_FILE = 'txt_file' + OUTPUT_SOURCE_GIVEN_VER_HEX_FILE = 'hex_version_file' + OUTPUT_SOURCE_GIVEN_VER_HEX_ADDR = 'hex_version_getreg' + + SET_METHOD_IPMI = 'ipmitool' + NULL_VAL = 'N/A' + HOST_CHK_CMD = "docker > /dev/null 2>&1" + REF_KEY = '$ref:' + + def __init__(self, conf=None): + self._main_conf = conf + (self.platform, self.hwsku) = device_info.get_platform_and_hwsku() + + def _run_command(self, command): + status = False + output = "" + try: + p = subprocess.Popen( + command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + status, output = True, raw_data.strip() + except Exception: + pass + return status, output + + def _clean_input(self, input, config): + cleaned_input = input + + ai = config.get('avaliable_input') + if ai and input not in ai: + return None + + input_translator = config.get('input_translator') + if type(input_translator) is dict: + cleaned_input = input_translator.get(input) + + elif type(input_translator) is str: + cleaned_input = eval(input_translator.format(input)) + + return cleaned_input + + def _clean_output(self, index, output, config): + output_translator = config.get('output_translator') + + if type(output_translator) is dict: + output = output_translator.get(output) + elif type(output_translator) is str: + output = eval(output_translator.format(output)) + elif type(output_translator) is list: + output = eval(output_translator[index].format(output)) + + return output + + def _ipmi_get(self, index, config): + argument = config.get('argument') + cmd = config['command'].format( + config['argument'][index]) if argument else config['command'] + status, output = self._run_command(cmd) + return output if status else None + + def _sysfs_read(self, index, config): + sysfs_path = config.get('sysfs_path') + argument = config.get('argument', '') + + if self.REF_KEY in argument: + argument = self._main_conf[argument.split(":")[1]] + + if type(argument) is list: + sysfs_path = sysfs_path.format(argument[index]) + + content = "" + try: + content = open(sysfs_path) + content = content.readline().rstrip() + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + + return content + + def _sysfs_write(self, index, config, input): + sysfs_path = config.get('sysfs_path') + argument = config.get('argument', '') + + if self.REF_KEY in argument: + argument = self._main_conf[argument.split(":")[1]] + + if type(argument) is list: + sysfs_path = sysfs_path.format(argument[index]) + + write_offset = int(config.get('write_offset', 0)) + output = "" + try: + open_file = open(sysfs_path, "r+") + open_file.seek(write_offset) + open_file.write(input) + open_file.close() + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False, output + return True, output + + def _ipmi_set(self, index, config, input): + arg = config['argument'][index].format(input) + return self._run_command(config['command'].format(arg)) + + def _hex_ver_decode(self, hver, num_of_bits, num_of_points): + ver_list = [] + c_bit = 0 + bin_val = bin(int(hver, 16))[2:].zfill(num_of_bits) + bit_split = num_of_bits / (num_of_points + 1) + for x in range(0, num_of_points+1): + split_bin = bin_val[c_bit:c_bit+bit_split] + ver_list.append(str(int(split_bin, 2))) + c_bit += bit_split + return '.'.join(ver_list) + + def _get_class(self, config): + """ + Retreives value of expected attribute + Returns: + A value of the attribute of object + """ + path = config['host_path'] if self.is_host() else config['pmon_path'] + module = imp.load_source(config['class'], path) + class_ = getattr(module, config['class']) + return class_ + + def get_reg(self, path, reg_addr): + cmd = "echo {1} > {0}; cat {0}".format(path, reg_addr) + status, output = self._run_command(cmd) + return output if status else None + + def read_txt_file(self, path): + with open(path, 'r') as f: + output = f.readline() + return output.strip('\n') + + def write_txt_file(self, file_path, value): + try: + with open(file_path, 'w') as fd: + fd.write(str(value)) + except Exception: + return False + return True + + def is_host(self): + return os.system(self.HOST_CHK_CMD) == 0 + + def load_json_file(self, path): + """ + Retrieves the json object from json file path + + Returns: + A json object + """ + with open(path, 'r') as f: + json_data = yaml.safe_load(f) + + return json_data + + def get_config_path(self, config_name): + """ + Retrieves the path to platform api config directory + + Args: + config_name: A string containing the name of config file. + + Returns: + A string containing the path to json file + """ + return os.path.join(self.DEVICE_PATH, self.platform, self.CONFIG_DIR, config_name) if self.is_host() else os.path.join(self.PMON_PLATFORM_PATH, self.CONFIG_DIR, config_name) + + def get_output(self, index, config, default): + """ + Retrieves the output for each function base on config + + Args: + index: An integer containing the index of device. + config: A dict object containing the configuration of specified function. + default: A string containing the default output of specified function. + + Returns: + A string containing the output of specified function in config + """ + output_source = config.get('output_source') + + if output_source == self.OUTPUT_SOURCE_IPMI: + output = self._ipmi_get(index, config) + + elif output_source == self.OUTPUT_SOURCE_GIVEN_VALUE: + output = config["value"] + + elif output_source == self.OUTPUT_SOURCE_GIVEN_CLASS: + output = self._get_class(config) + + elif output_source == self.OUTPUT_SOURCE_GIVEN_LIST: + output = config["value_list"][index] + + elif output_source == self.OUTPUT_SOURCE_SYSFS: + output = self._sysfs_read(index, config) + + elif output_source == self.OUTPUT_SOURCE_FUNC: + func_conf = self._main_conf[config['function'][index]] + output = self.get_output(index, func_conf, default) + + elif output_source == self.OUTPUT_SOURCE_GIVEN_TXT_FILE: + path = config.get('path') + output = self.read_txt_file(path) + + elif output_source == self.OUTPUT_SOURCE_GIVEN_VER_HEX_FILE: + path = config.get('path') + hex_ver = self.read_txt_file(path) + output = self._hex_ver_decode( + hex_ver, config['num_of_bits'], config['num_of_points']) + + elif output_source == self.OUTPUT_SOURCE_GIVEN_VER_HEX_ADDR: + path = config.get('path') + addr = config.get('reg_addr') + hex_ver = self.get_reg(path, addr) + output = self._hex_ver_decode( + hex_ver, config['num_of_bits'], config['num_of_points']) + + else: + output = default + + return self._clean_output(index, output, config) or default + + def set_output(self, index, input, config): + """ + Sets the output of specified function on config + + Args: + config: A dict object containing the configuration of specified function. + index: An integer containing the index of device. + input: A string containing the input of specified function. + + Returns: + bool: True if set function is successfully, False if not + """ + cleaned_input = self._clean_input(input, config) + if not cleaned_input: + return False + + set_method = config.get('set_method') + if set_method == self.SET_METHOD_IPMI: + output = self._ipmi_set(index, config, cleaned_input)[0] + elif set_method == self.OUTPUT_SOURCE_SYSFS: + output = self._sysfs_write(index, config, cleaned_input)[0] + else: + output = False + + return output + + def get_event(self, timeout, config, sfp_list): + """ + Returns a nested dictionary containing all devices which have + experienced a change at chassis level + + """ + event_class = self._get_class(config) + return event_class(sfp_list).get_event(timeout) diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/component.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/component.py new file mode 100644 index 000000000000..958b34623222 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/component.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Component contains an implementation of SONiC Platform Base API and +# provides the components firmware management function +# +############################################################################# + +try: + from sonic_platform_base.component_base import ComponentBase + from common import Common +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Component(ComponentBase): + """Platform-specific Component class""" + + def __init__(self, component_index, conf): + ComponentBase.__init__(self) + + self._index = component_index + self._config = conf + self._api_common = Common(self._config) + + def get_name(self): + """ + Retrieves the name of the component + Returns: + A string containing the name of the component + """ + default = 'N/A' + config = self._config["get_name"] + return self._api_common.get_output(self._index, config, default) + + def get_description(self): + """ + Retrieves the description of the component + Returns: + A string containing the description of the component + """ + default = 'N/A' + config = self._config["get_description"] + return self._api_common.get_output(self._index, config, default) + + def get_firmware_version(self): + """ + Retrieves the firmware version of the component + Note: the firmware version will be read from HW + Returns: + A string containing the firmware version of the component + """ + default = 'N/A' + config = self._config["get_firmware_version"] + return self._api_common.get_output(self._index, config, default) diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/eeprom.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/eeprom.py new file mode 100644 index 000000000000..7756c0f2f839 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/eeprom.py @@ -0,0 +1,113 @@ +############################################################################# +# Celestica +# +# Platform and model specific eeprom subclass, inherits from the base class, +# and provides the followings: +# - the eeprom format definition +# - specific encoder/decoder if there is special need +############################################################################# + +try: + import os + import sys + import re + + if sys.version_info.major == 3: + from io import StringIO + else: + from cStringIO import StringIO + + from sonic_platform_base.sonic_eeprom import eeprom_tlvinfo +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +CACHE_ROOT = '/var/cache/sonic/decode-syseeprom' +CACHE_FILE = 'syseeprom_cache' + + +class Tlv(eeprom_tlvinfo.TlvInfoDecoder): + + EEPROM_DECODE_HEADLINES = 6 + DEFAULT_EEPROM_PATH = "/sys/class/i2c-adapter/i2c-12/12-0050/eeprom" + + def __init__(self, conf=None): + eeprom_path = conf.get('eeprom', self.DEFAULT_EEPROM_PATH) + super(Tlv, self).__init__(eeprom_path, 0, '', True) + + self._eeprom = self._load_eeprom() + + def _parse_output(self, decode_output): + decode_output.replace('\0', '') + lines = decode_output.split('\n') + lines = lines[self.EEPROM_DECODE_HEADLINES:] + _eeprom_info_dict = dict() + + for line in lines: + try: + match = re.search( + '(0x[0-9a-fA-F]{2})([\s]+[\S]+[\s]+)([\S]+)', line) + if match is not None: + idx = match.group(1) + value = match.group(3).rstrip('\0') + + _eeprom_info_dict[idx] = value + except Exception: + pass + return _eeprom_info_dict + + def _load_eeprom(self): + original_stdout = sys.stdout + sys.stdout = StringIO() + try: + self.read_eeprom_db() + except Exception: + decode_output = sys.stdout.getvalue() + sys.stdout = original_stdout + return self._parse_output(decode_output) + + status = self.check_status() + if 'ok' not in status: + return False + + if not os.path.exists(CACHE_ROOT): + try: + os.makedirs(CACHE_ROOT) + except Exception: + pass + + # + # only the eeprom classes that inherit from eeprom_base + # support caching. Others will work normally + # + try: + self.set_cache_name(os.path.join(CACHE_ROOT, CACHE_FILE)) + except Exception: + pass + + e = self.read_eeprom() + if e is None: + return 0 + + try: + self.update_cache(e) + except Exception: + pass + + self.decode_eeprom(e) + decode_output = sys.stdout.getvalue() + sys.stdout = original_stdout + + # (is_valid, valid_crc) = self.is_checksum_valid(e) + # if not is_valid: + # return False + + return self._parse_output(decode_output) + + def get_eeprom(self): + return self._eeprom + + def get_serial(self): + return self._eeprom.get('0x23', "Undefined.") + + def get_mac(self): + return self._eeprom.get('0x24', "Undefined.") diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/fan.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/fan.py new file mode 100644 index 000000000000..b0ebebccd3d7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/fan.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Module contains an implementation of SONiC Platform Base API and +# provides the fan status which are available in the platform +# +############################################################################# + +try: + from sonic_platform_base.fan_base import FanBase + from common import Common +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Fan(FanBase): + """Platform-specific Fan class""" + + def __init__(self, index, is_psu_fan=False, psu_index=0, conf=None): + FanBase.__init__(self) + + self._fan_index = index + self._config = conf + self._api_common = Common() + + self._is_psu_fan = is_psu_fan + if self._is_psu_fan: + self._initialize_psu_fan(psu_index) + + self._name = self.get_name() + + def _initialize_psu_fan(self, psu_index): + self._psu_index = psu_index + self._psu_fan_config = self._config['psu_fan'][self._psu_index] + + def get_direction(self): + """ + Retrieves the direction of fan + Returns: + A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST + depending on fan direction + """ + return self._api_common.get_output(self._fan_index, self._config['get_direction'], self.FAN_DIRECTION_NOT_APPLICABLE) + + def get_speed(self): + """ + Retrieves the speed of fan as a percentage of full speed + Returns: + An integer, the percentage of full fan speed, in the range 0 (off) + to 100 (full speed) + + Note: + speed = pwm_in/255*100 + """ + config = self._config['get_speed'] if not self._is_psu_fan else self._psu_fan_config['get_speed'] + max_rpm = config['max_rear'] if 'R' in self._name else config['max_front'] + raw_speed = self._api_common.get_output( + self._fan_index, config, 0) + + return int(float(raw_speed) / max_rpm * 100.0) + + def get_target_speed(self): + """ + Retrieves the target (expected) speed of the fan + Returns: + An integer, the percentage of full fan speed, in the range 0 (off) + to 100 (full speed) + + Note: + 0 : when PWM mode is not in use + pwm : when pwm mode is not use + """ + return self._api_common.get_output(self._fan_index, self._config['get_target_speed'], Common.NULL_VAL) + + def get_speed_tolerance(self): + """ + Retrieves the speed tolerance of the fan + Returns: + An integer, the percentage of variance from target speed which is + considered tolerable + """ + return self._api_common.get_output(self._fan_index, self._config['get_speed_tolerance'], Common.NULL_VAL) + + def set_speed(self, speed): + """ + Sets the fan speed + Args: + speed: An integer, the percentage of full fan speed to set fan to, + in the range 0 (off) to 100 (full speed) + Returns: + A boolean, True if speed is set successfully, False if not + + Note: + Depends on pwm or target mode is selected: + 1) pwm = speed_pc * 255 <-- Currently use this mode. + 2) target_pwm = speed_pc * 100 / 255 + 2.1) set pwm{}_enable to 3 + + ipmitool raw 0x3a 0x0e 0x00 > enable auto fcs + ipmitool raw 0x3a 0x0e 0x01 > disable auto fcs + """ + if speed not in range(1, 101) or self._is_psu_fan: + return False + + return self._api_common.set_output(self._fan_index, speed, self._config['set_speed']) + + def set_status_led(self, color): + """ + Sets the state of the fan module status LED + Args: + color: A string representing the color with which to set the + fan module status LED + Returns: + bool: True if status LED state is set successfully, False if not + + + Note: Required Manual Control LED mode ('ipmitool raw 0x3a 0x0f 0x2 0x0') + """ + config = self._config['set_status_led'] + avaliable_input = config.get('avaliable_input') + if (avaliable_input and color not in avaliable_input) or self._is_psu_fan: + return False + + return self._api_common.set_output(self._fan_index, color, config) + + def get_status_led(self): + """ + Gets the state of the fan status LED + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + + Note: + STATUS_LED_COLOR_GREEN = "green" + STATUS_LED_COLOR_AMBER = "amber" + STATUS_LED_COLOR_RED = "red" + STATUS_LED_COLOR_OFF = "off" + """ + + default = self.STATUS_LED_COLOR_OFF + + if self._is_psu_fan: + return default + + return self._api_common.get_output(self._fan_index, self._config['get_status_led'], default) + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + config = self._config['get_name'] if not self._is_psu_fan else self._psu_fan_config['get_name'] + return self._api_common.get_output(self._fan_index, config, Common.NULL_VAL) + + def get_presence(self): + """ + Retrieves the presence of the FAN + Returns: + bool: True if FAN is present, False if not + """ + return self._api_common.get_output(self._fan_index, self._config['get_presence'], False) + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + default = Common.NULL_VAL + if self._is_psu_fan: + return default + return self._api_common.get_output(self._fan_index, self._config['get_model'], default) + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + default = Common.NULL_VAL + if self._is_psu_fan: + return default + return self._api_common.get_output(self._fan_index, self._config['get_serial'], default) + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self.get_presence() and self.get_speed() > 10 diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/fan_drawer.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/fan_drawer.py new file mode 100644 index 000000000000..5ef283a105d4 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/fan_drawer.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Module contains an implementation of SONiC Platform Base API and +# provides the fan status which are available in the platform +# +############################################################################# + +try: + from sonic_platform_base.fan_drawer_base import FanDrawerBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class FanDrawer(FanDrawerBase): + + def __init__(self, index, fan_list): + FanDrawerBase.__init__(self) + + self._fan_list = fan_list + self._index = index + 1 + + def set_status_led(self, color): + """ + Sets the state of the fan drawer status LED + Args: + color: A string representing the color with which to set the + fan drawer status LED + Returns: + bool: True if status LED state is set successfully, False if not + """ + return self._fan_list[0].set_status_led(color) + + def get_status_led(self, color): + """ + Gets the state of the fan drawer LED + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + return self._fan_list[0].get_status_led() + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + return 'Fan {}'.format(self._index) + + def get_presence(self): + """ + Retrieves the presence of the FAN + Returns: + bool: True if FAN is present, False if not + """ + return self._fan_list[0].get_presence() + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + return self._fan_list[0].get_model() + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return self._fan_list[0].get_serial() + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self._fan_list[0].get_status() diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/platform.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/platform.py new file mode 100644 index 000000000000..a632de87e742 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/platform.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Module contains an implementation of SONiC Platform Base API and +# provides the platform information +# +############################################################################# + +try: + from sonic_platform_base.platform_base import PlatformBase + from sonic_platform.chassis import Chassis +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Platform(PlatformBase): + """Platform-specific Platform class""" + + def __init__(self): + PlatformBase.__init__(self) + self._chassis = Chassis() diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/psu.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/psu.py new file mode 100644 index 000000000000..68dfe201d33f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/psu.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Module contains an implementation of SONiC Platform Base API and +# provides the psu status which are available in the platform +# +############################################################################# + +try: + from sonic_platform_base.psu_base import PsuBase + from common import Common +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Psu(PsuBase): + """Platform-specific Fan class""" + + def __init__(self, index, conf=None, fan_conf=None): + PsuBase.__init__(self) + + self._psu_index = index + self._config = conf + self._api_common = Common(self._config) + self._fan_conf = fan_conf + self._initialize_psu_fan() + + def _initialize_psu_fan(self): + from sonic_platform.fan import Fan + + num_fan = self._fan_conf['psu_fan'][self._psu_index]["num_of_fan"] + for fan_index in range(0, num_fan): + fan = Fan(fan_index, is_psu_fan=True, + psu_index=self._psu_index, conf=self._fan_conf) + self._fan_list.append(fan) + + def get_voltage(self): + """ + Retrieves current PSU voltage output + + Returns: + A float number, the output voltage in volts, + e.g. 12.1 + """ + return self._api_common.get_output(self._psu_index, self._config['get_voltage'], 0.0) + + def get_current(self): + """ + Retrieves present electric current supplied by PSU + + Returns: + A float number, the electric current in amperes, e.g 15.4 + """ + return self._api_common.get_output(self._psu_index, self._config['get_current'], 0.0) + + def get_power(self): + """ + Retrieves current energy supplied by PSU + + Returns: + A float number, the power in watts, e.g. 302.6 + """ + return self._api_common.get_output(self._psu_index, self._config['get_current'], 0.0) + + def get_powergood_status(self): + """ + Retrieves the powergood status of PSU + + Returns: + A boolean, True if PSU has stablized its output voltages and passed all + its internal self-tests, False if not. + """ + return self._api_common.get_output(self._psu_index, self._config['get_powergood_status'], False) + + def set_status_led(self, color): + """ + Sets the state of the PSU status LED + Note: + Seastone2 CPLD able to set only AMBER color. + This function should be disable auto mode before execute + command: ipmitool raw 0x3a 0x0f 0x02 0x00 + Args: + color: A string representing the color with which to set the + PSU status LED + + Returns: + bool: True if status LED state is set successfully, False if not + """ + return self._api_common.set_output(self._psu_index, color, self._config['set_status_led']) + + def get_status_led(self): + """ + Gets the state of the PSU status LED + Note: + Seastone2 PSU LED got only 2 mode, AMBER and Hardware control mode. + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + return self._api_common.get_output(self._psu_index, self._config['get_status_led'], Common.NULL_VAL) + + def get_temperature(self): + """ + Retrieves current temperature reading from PSU + + Returns: + A float number of current temperature in Celsius up to nearest thousandth + of one degree Celsius, e.g. 30.125 + """ + return self._api_common.get_output(self._psu_index, self._config['get_temperature'], 0.0) + + def get_temperature_high_threshold(self): + """ + Retrieves the high threshold temperature of PSU + Returns: + A float number, the high threshold temperature of PSU in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + return self._api_common.get_output(self._psu_index, self._config['get_temperature_high_threshold'], 0.0) + + def get_voltage_high_threshold(self): + """ + Retrieves the high threshold PSU voltage output + + Returns: + ucr as float number and return 0 if the BMC output is na. + A float number, the high threshold output voltage in volts, + e.g. 12.1 + """ + return self._api_common.get_output(self._psu_index, self._config['get_voltage_high_threshold'], 0.0) + + def get_voltage_low_threshold(self): + """ + Retrieves the low threshold PSU voltage output + + Returns: + lcr as float number and return 0 if the BMC output is na. + A float number, the low threshold output voltage in volts, + e.g. 12.1 + """ + return self._api_common.get_output(self._psu_index, self._config['get_voltage_low_threshold'], 0.0) + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + return self._api_common.get_output(self._psu_index, self._config['get_name'], Common.NULL_VAL) + + def get_presence(self): + """ + Retrieves the presence of the PSU + Returns: + bool: True if PSU is present, False if not + """ + return self._api_common.get_output(self._psu_index, self._config['get_name'], False) + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + return self._api_common.get_output(self._psu_index, self._config['get_model'], Common.NULL_VAL) + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return self._api_common.get_output(self._psu_index, self._config['get_serial'], Common.NULL_VAL) + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self.get_powergood_status() diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/sfp.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/sfp.py new file mode 100644 index 000000000000..07eac37d8d82 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/sfp.py @@ -0,0 +1,2037 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Module contains an implementation of SONiC Platform Base API and +# provides the sfp status which are available in the platform +# +############################################################################# + +import time +from ctypes import create_string_buffer + +try: + from sonic_platform_base.sfp_base import SfpBase + from sonic_platform_base.sonic_sfp.sff8472 import sff8472InterfaceId + from sonic_platform_base.sonic_sfp.sff8472 import sff8472Dom + from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId + from sonic_platform_base.sonic_sfp.sff8436 import sff8436Dom + from sonic_platform_base.sonic_sfp.inf8628 import inf8628InterfaceId + from sonic_platform_base.sonic_sfp.qsfp_dd import qsfp_dd_InterfaceId + from sonic_platform_base.sonic_sfp.qsfp_dd import qsfp_dd_Dom + from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper + from common import Common +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +# definitions of the offset and width for values in XCVR info eeprom +XCVR_INTFACE_BULK_OFFSET = 0 +XCVR_INTFACE_BULK_WIDTH_QSFP = 20 +XCVR_INTFACE_BULK_WIDTH_SFP = 21 +XCVR_TYPE_OFFSET = 0 +XCVR_TYPE_WIDTH = 1 +XCVR_EXT_TYPE_OFFSET = 1 +XCVR_EXT_TYPE_WIDTH = 1 +XCVR_CONNECTOR_OFFSET = 2 +XCVR_CONNECTOR_WIDTH = 1 +XCVR_COMPLIANCE_CODE_OFFSET = 3 +XCVR_COMPLIANCE_CODE_WIDTH = 8 +XCVR_ENCODING_OFFSET = 11 +XCVR_ENCODING_WIDTH = 1 +XCVR_NBR_OFFSET = 12 +XCVR_NBR_WIDTH = 1 +XCVR_EXT_RATE_SEL_OFFSET = 13 +XCVR_EXT_RATE_SEL_WIDTH = 1 +XCVR_CABLE_LENGTH_OFFSET = 14 +XCVR_CABLE_LENGTH_WIDTH_QSFP = 5 +XCVR_CABLE_LENGTH_WIDTH_SFP = 6 +XCVR_VENDOR_NAME_OFFSET = 20 +XCVR_VENDOR_NAME_WIDTH = 16 +XCVR_VENDOR_OUI_OFFSET = 37 +XCVR_VENDOR_OUI_WIDTH = 3 +XCVR_VENDOR_PN_OFFSET = 40 +XCVR_VENDOR_PN_WIDTH = 16 +XCVR_HW_REV_OFFSET = 56 +XCVR_HW_REV_WIDTH_OSFP = 2 +XCVR_HW_REV_WIDTH_QSFP = 2 +XCVR_HW_REV_WIDTH_SFP = 4 +XCVR_EXT_SPECIFICATION_COMPLIANCE_OFFSET = 64 +XCVR_EXT_SPECIFICATION_COMPLIANCE_WIDTH = 1 +XCVR_VENDOR_SN_OFFSET = 68 +XCVR_VENDOR_SN_WIDTH = 16 +XCVR_VENDOR_DATE_OFFSET = 84 +XCVR_VENDOR_DATE_WIDTH = 8 +XCVR_DOM_CAPABILITY_OFFSET = 92 +XCVR_DOM_CAPABILITY_WIDTH = 2 + +XCVR_INTERFACE_DATA_START = 0 +XCVR_INTERFACE_DATA_SIZE = 92 + +QSFP_DOM_BULK_DATA_START = 22 +QSFP_DOM_BULK_DATA_SIZE = 36 +SFP_DOM_BULK_DATA_START = 96 +SFP_DOM_BULK_DATA_SIZE = 10 +QSFP_DD_DOM_BULK_DATA_START = 14 +QSFP_DD_DOM_BULK_DATA_SIZE = 4 + +# definitions of the offset for values in OSFP info eeprom +OSFP_TYPE_OFFSET = 0 +OSFP_VENDOR_NAME_OFFSET = 129 +OSFP_VENDOR_PN_OFFSET = 148 +OSFP_HW_REV_OFFSET = 164 +OSFP_VENDOR_SN_OFFSET = 166 + +# definitions of the offset for values in QSFP_DD info eeprom +QSFP_DD_TYPE_OFFSET = 0 +QSFP_DD_VENDOR_NAME_OFFSET = 1 +QSFP_DD_VENDOR_PN_OFFSET = 20 +QSFP_DD_VENDOR_SN_OFFSET = 38 +QSFP_DD_VENDOR_OUI_OFFSET = 17 + +# definitions of the offset and width for values in XCVR_QSFP_DD info eeprom +XCVR_EXT_TYPE_OFFSET_QSFP_DD = 72 +XCVR_EXT_TYPE_WIDTH_QSFP_DD = 2 +XCVR_CONNECTOR_OFFSET_QSFP_DD = 75 +XCVR_CONNECTOR_WIDTH_QSFP_DD = 1 +XCVR_CABLE_LENGTH_OFFSET_QSFP_DD = 74 +XCVR_CABLE_LENGTH_WIDTH_QSFP_DD = 1 +XCVR_HW_REV_OFFSET_QSFP_DD = 36 +XCVR_HW_REV_WIDTH_QSFP_DD = 2 +XCVR_VENDOR_DATE_OFFSET_QSFP_DD = 54 +XCVR_VENDOR_DATE_WIDTH_QSFP_DD = 8 +XCVR_DOM_CAPABILITY_OFFSET_QSFP_DD = 2 +XCVR_DOM_CAPABILITY_WIDTH_QSFP_DD = 1 +XCVR_MEDIA_TYPE_OFFSET_QSFP_DD = 85 +XCVR_MEDIA_TYPE_WIDTH_QSFP_DD = 1 +XCVR_FIRST_APPLICATION_LIST_OFFSET_QSFP_DD = 86 +XCVR_FIRST_APPLICATION_LIST_WIDTH_QSFP_DD = 32 +XCVR_SECOND_APPLICATION_LIST_OFFSET_QSFP_DD = 351 +XCVR_SECOND_APPLICATION_LIST_WIDTH_QSFP_DD = 28 + +# Offset for values in QSFP eeprom +QSFP_DOM_REV_OFFSET = 1 +QSFP_DOM_REV_WIDTH = 1 +QSFP_TEMPE_OFFSET = 22 +QSFP_TEMPE_WIDTH = 2 +QSFP_VOLT_OFFSET = 26 +QSFP_VOLT_WIDTH = 2 +QSFP_VERSION_COMPLIANCE_OFFSET = 1 +QSFP_VERSION_COMPLIANCE_WIDTH = 2 +QSFP_CHANNL_MON_OFFSET = 34 +QSFP_CHANNL_MON_WIDTH = 16 +QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH = 24 +QSFP_CHANNL_DISABLE_STATUS_OFFSET = 86 +QSFP_CHANNL_DISABLE_STATUS_WIDTH = 1 +QSFP_CHANNL_RX_LOS_STATUS_OFFSET = 3 +QSFP_CHANNL_RX_LOS_STATUS_WIDTH = 1 +QSFP_CHANNL_TX_FAULT_STATUS_OFFSET = 4 +QSFP_CHANNL_TX_FAULT_STATUS_WIDTH = 1 +QSFP_CONTROL_OFFSET = 86 +QSFP_CONTROL_WIDTH = 8 +QSFP_MODULE_MONITOR_OFFSET = 0 +QSFP_MODULE_MONITOR_WIDTH = 9 +QSFP_POWEROVERRIDE_OFFSET = 93 +QSFP_POWEROVERRIDE_WIDTH = 1 +QSFP_POWEROVERRIDE_BIT = 0 +QSFP_POWERSET_BIT = 1 +QSFP_OPTION_VALUE_OFFSET = 192 +QSFP_OPTION_VALUE_WIDTH = 4 +QSFP_MODULE_UPPER_PAGE3_START = 384 +QSFP_MODULE_THRESHOLD_OFFSET = 128 +QSFP_MODULE_THRESHOLD_WIDTH = 24 +QSFP_CHANNL_THRESHOLD_OFFSET = 176 +QSFP_CHANNL_THRESHOLD_WIDTH = 24 + +SFP_MODULE_ADDRA2_OFFSET = 256 +SFP_MODULE_THRESHOLD_OFFSET = 0 +SFP_MODULE_THRESHOLD_WIDTH = 56 +SFP_CHANNL_THRESHOLD_OFFSET = 112 +SFP_CHANNL_THRESHOLD_WIDTH = 2 + +SFP_TEMPE_OFFSET = 96 +SFP_TEMPE_WIDTH = 2 +SFP_VOLT_OFFSET = 98 +SFP_VOLT_WIDTH = 2 +SFP_CHANNL_MON_OFFSET = 100 +SFP_CHANNL_MON_WIDTH = 6 +SFP_CHANNL_STATUS_OFFSET = 110 +SFP_CHANNL_STATUS_WIDTH = 1 + +QSFP_DD_TEMPE_OFFSET = 14 +QSFP_DD_TEMPE_WIDTH = 2 +QSFP_DD_VOLT_OFFSET = 16 +QSFP_DD_VOLT_WIDTH = 2 +QSFP_DD_TX_BIAS_OFFSET = 42 +QSFP_DD_TX_BIAS_WIDTH = 16 +QSFP_DD_RX_POWER_OFFSET = 58 +QSFP_DD_RX_POWER_WIDTH = 16 +QSFP_DD_TX_POWER_OFFSET = 26 +QSFP_DD_TX_POWER_WIDTH = 16 +QSFP_DD_CHANNL_MON_OFFSET = 154 +QSFP_DD_CHANNL_MON_WIDTH = 48 +QSFP_DD_CHANNL_DISABLE_STATUS_OFFSET = 86 +QSFP_DD_CHANNL_DISABLE_STATUS_WIDTH = 1 +QSFP_DD_CHANNL_RX_LOS_STATUS_OFFSET = 19 +QSFP_DD_CHANNL_RX_LOS_STATUS_WIDTH = 1 +QSFP_DD_CHANNL_TX_FAULT_STATUS_OFFSET = 7 +QSFP_DD_CHANNL_TX_FAULT_STATUS_WIDTH = 1 +QSFP_DD_MODULE_THRESHOLD_OFFSET = 0 +QSFP_DD_MODULE_THRESHOLD_WIDTH = 72 +QSFP_DD_CHANNL_STATUS_OFFSET = 26 +QSFP_DD_CHANNL_STATUS_WIDTH = 1 + + +sfp_cable_length_tup = ('LengthSMFkm-UnitsOfKm', 'LengthSMF(UnitsOf100m)', + 'Length50um(UnitsOf10m)', 'Length62.5um(UnitsOfm)', + 'LengthCable(UnitsOfm)', 'LengthOM3(UnitsOf10m)') + +sfp_compliance_code_tup = ('10GEthernetComplianceCode', 'InfinibandComplianceCode', + 'ESCONComplianceCodes', 'SONETComplianceCodes', + 'EthernetComplianceCodes', 'FibreChannelLinkLength', + 'FibreChannelTechnology', 'SFP+CableTechnology', + 'FibreChannelTransmissionMedia', 'FibreChannelSpeed') + +qsfp_compliance_code_tup = ('10/40G Ethernet Compliance Code', 'SONET Compliance codes', + 'SAS/SATA compliance codes', 'Gigabit Ethernet Compliant codes', + 'Fibre Channel link length/Transmitter Technology', + 'Fibre Channel transmission media', 'Fibre Channel Speed') + +info_dict_keys = ['type', 'hardwarerev', 'serial', 'manufacturer', 'model', 'connector', 'encoding', 'ext_identifier', + 'ext_rateselect_compliance', 'cable_type', 'cable_length', 'nominal_bit_rate', 'specification_compliance', + 'vendor_date', 'vendor_oui', 'application_advertisement'] + +qsfp_cable_length_tup = ('Length(km)', 'Length OM3(2m)', + 'Length OM2(m)', 'Length OM1(m)', + 'Length Cable Assembly(m)') + +dom_info_dict_keys = ['rx_los', 'tx_fault', 'reset_status', 'lp_mode', 'tx_disable', 'tx_disable_channel', 'temperature', 'voltage', + 'rx1power', 'rx2power', 'rx3power', 'rx4power', 'rx5power', 'rx6power', 'rx7power', 'rx8power', + 'tx1bias', 'tx2bias', 'tx3bias', 'tx4bias', 'tx5bias', 'tx6bias', 'tx7bias', 'tx8bias', + 'tx1power', 'tx2power', 'tx3power', 'tx4power', 'tx5power', 'tx6power', 'tx7power', 'tx8power'] + +threshold_dict_keys = ['temphighalarm', 'temphighwarning', 'templowalarm', 'templowwarning', 'vcchighalarm', 'vcchighwarning', + 'vcclowalarm', 'vcclowwarning', 'rxpowerhighalarm', 'rxpowerhighwarning', + 'rxpowerlowalarm', 'rxpowerlowwarning', 'txpowerhighalarm', 'txpowerhighwarning', 'txpowerlowalarm', + 'txpowerlowwarning', 'txbiashighalarm', 'txbiashighwarning', 'txbiaslowalarm', 'txbiaslowwarning'] + +SFP_TYPE_CODE_LIST = [ + '03' # SFP/SFP+/SFP28 +] +QSFP_TYPE_CODE_LIST = [ + '0d', # QSFP+ or later + '11' # QSFP28 or later +] +QSFP_DD_TYPE_CODE_LIST = [ + '18' # QSFP-DD Double Density 8X Pluggable Transceiver +] + +SFP_TYPE = "SFP" +QSFP_TYPE = "QSFP" +OSFP_TYPE = "OSFP" +QSFP_DD_TYPE = "QSFP_DD" + + +class Sfp(SfpBase): + """Platform-specific Sfp class""" + + def __init__(self, sfp_index=0, conf=None): + SfpBase.__init__(self) + + self._sfp_index = sfp_index + self._config = conf + self._api_common = Common(self._config) + + self._read_porttab_mappings() + self._dom_capability_detect() + + def _read_porttab_mappings(self): + self._sfputil_helper = SfpUtilHelper() + self._sfputil_helper.read_porttab_mappings( + self._get_path_to_port_config_file()) + + def _get_path_to_port_config_file(self): + host_platform_root_path = '/usr/share/sonic/device' + docker_hwsku_path = '/usr/share/sonic/hwsku' + + host_platform_path = "/".join([host_platform_root_path, + self._api_common.platform]) + hwsku_path = "/".join([host_platform_path, self._api_common.hwsku] + ) if self._api_common.is_host() else docker_hwsku_path + + return "/".join([hwsku_path, "port_config.ini"]) + + def _convert_string_to_num(self, value_str): + if "-inf" in value_str: + return Common.NULL_VAL + elif "Unknown" in value_str: + return Common.NULL_VAL + elif 'dBm' in value_str: + t_str = value_str.rstrip('dBm') + return float(t_str) + elif 'mA' in value_str: + t_str = value_str.rstrip('mA') + return float(t_str) + elif 'C' in value_str: + t_str = value_str.rstrip('C') + return float(t_str) + elif 'Volts' in value_str: + t_str = value_str.rstrip('Volts') + return float(t_str) + else: + return Common.NULL_VAL + + def _read_eeprom_specific_bytes(self, offset, num_bytes): + sysfsfile_eeprom = None + eeprom_raw = [] + for i in range(0, num_bytes): + eeprom_raw.append("0x00") + + sysfs_sfp_i2c_client_eeprom_path = self._get_eeprom_path() + try: + sysfsfile_eeprom = open( + sysfs_sfp_i2c_client_eeprom_path, mode="rb", buffering=0) + sysfsfile_eeprom.seek(offset) + raw = sysfsfile_eeprom.read(num_bytes) + for n in range(0, num_bytes): + eeprom_raw[n] = hex(ord(raw[n]))[2:].zfill(2) + except Exception: + pass + finally: + if sysfsfile_eeprom: + sysfsfile_eeprom.close() + + return eeprom_raw + + def _detect_sfp_type(self): + sfp_type = QSFP_TYPE + eeprom_raw = [] + eeprom_raw = self._read_eeprom_specific_bytes( + XCVR_TYPE_OFFSET, XCVR_TYPE_WIDTH) + if eeprom_raw: + if eeprom_raw[0] in SFP_TYPE_CODE_LIST: + self.sfp_type = SFP_TYPE + elif eeprom_raw[0] in QSFP_TYPE_CODE_LIST: + self.sfp_type = QSFP_TYPE + elif eeprom_raw[0] in QSFP_DD_TYPE_CODE_LIST: + self.sfp_type = QSFP_DD_TYPE + else: + self.sfp_type = sfp_type + else: + self.sfp_type = sfp_type + + def _get_eeprom_path(self): + eeprom_path = self._config['eeprom_path'] + port_to_i2c_mapping = self._config['port_i2c_mapping'] + port_eeprom_path = eeprom_path.format( + port_to_i2c_mapping[self._sfp_index]) + + return port_eeprom_path + + def _dom_capability_detect(self): + if not self.get_presence(): + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.calibration = 0 + return + + self._detect_sfp_type() + + if self.sfp_type == QSFP_TYPE: + self.calibration = 1 + sfpi_obj = sff8436InterfaceId() + if sfpi_obj is None: + self.dom_supported = False + offset = 128 + + # QSFP capability byte parse, through this byte can know whether it support tx_power or not. + # TODO: in the future when decided to migrate to support SFF-8636 instead of SFF-8436, + # need to add more code for determining the capability and version compliance + # in SFF-8636 dom capability definitions evolving with the versions. + qsfp_dom_capability_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_DOM_CAPABILITY_OFFSET), XCVR_DOM_CAPABILITY_WIDTH) + if qsfp_dom_capability_raw is not None: + qsfp_version_compliance_raw = self._read_eeprom_specific_bytes( + QSFP_VERSION_COMPLIANCE_OFFSET, QSFP_VERSION_COMPLIANCE_WIDTH) + qsfp_version_compliance = int( + qsfp_version_compliance_raw[0], 16) + dom_capability = sfpi_obj.parse_dom_capability( + qsfp_dom_capability_raw, 0) + if qsfp_version_compliance >= 0x08: + self.dom_temp_supported = dom_capability['data']['Temp_support']['value'] == 'On' + self.dom_volt_supported = dom_capability['data']['Voltage_support']['value'] == 'On' + self.dom_rx_power_supported = dom_capability['data']['Rx_power_support']['value'] == 'On' + self.dom_tx_power_supported = dom_capability['data']['Tx_power_support']['value'] == 'On' + else: + self.dom_temp_supported = True + self.dom_volt_supported = True + self.dom_rx_power_supported = dom_capability['data']['Rx_power_support']['value'] == 'On' + self.dom_tx_power_supported = True + + self.dom_supported = True + self.calibration = 1 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return None + qsfp_option_value_raw = self._read_eeprom_specific_bytes( + QSFP_OPTION_VALUE_OFFSET, QSFP_OPTION_VALUE_WIDTH) + if qsfp_option_value_raw is not None: + optional_capability = sfpd_obj.parse_option_params( + qsfp_option_value_raw, 0) + self.dom_tx_disable_supported = optional_capability[ + 'data']['TxDisable']['value'] == 'On' + dom_status_indicator = sfpd_obj.parse_dom_status_indicator( + qsfp_version_compliance_raw, 1) + self.qsfp_page3_available = dom_status_indicator['data']['FlatMem']['value'] == 'Off' + else: + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.calibration = 0 + self.qsfp_page3_available = False + + elif self.sfp_type == QSFP_DD_TYPE: + sfpi_obj = qsfp_dd_InterfaceId() + if sfpi_obj is None: + self.dom_supported = False + + offset = 0 + # two types of QSFP-DD cable types supported: Copper and Optical. + qsfp_dom_capability_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_DOM_CAPABILITY_OFFSET_QSFP_DD), XCVR_DOM_CAPABILITY_WIDTH_QSFP_DD) + if qsfp_dom_capability_raw is not None: + self.dom_temp_supported = True + self.dom_volt_supported = True + dom_capability = sfpi_obj.parse_dom_capability( + qsfp_dom_capability_raw, 0) + if dom_capability['data']['Flat_MEM']['value'] == 'Off': + self.dom_supported = True + self.second_application_list = True + self.dom_rx_power_supported = True + self.dom_tx_power_supported = True + self.dom_tx_bias_power_supported = True + self.dom_thresholds_supported = True + # currently set to False becasue Page 11h is not supported by FW + self.dom_rx_tx_power_bias_supported = False + else: + self.dom_supported = False + self.second_application_list = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.dom_tx_bias_power_supported = False + self.dom_thresholds_supported = False + self.dom_rx_tx_power_bias_supported = False + else: + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.dom_tx_bias_power_supported = False + self.dom_thresholds_supported = False + self.dom_rx_tx_power_bias_supported = False + + elif self.sfp_type == SFP_TYPE: + sfpi_obj = sff8472InterfaceId() + if sfpi_obj is None: + return None + sfp_dom_capability_raw = self._read_eeprom_specific_bytes( + XCVR_DOM_CAPABILITY_OFFSET, XCVR_DOM_CAPABILITY_WIDTH) + if sfp_dom_capability_raw is not None: + sfp_dom_capability = int(sfp_dom_capability_raw[0], 16) + self.dom_supported = (sfp_dom_capability & 0x40 != 0) + if self.dom_supported: + self.dom_temp_supported = True + self.dom_volt_supported = True + self.dom_rx_power_supported = True + self.dom_tx_power_supported = True + if sfp_dom_capability & 0x20 != 0: + self.calibration = 1 + elif sfp_dom_capability & 0x10 != 0: + self.calibration = 2 + else: + self.calibration = 0 + else: + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.calibration = 0 + self.dom_tx_disable_supported = ( + int(sfp_dom_capability_raw[1], 16) & 0x40 != 0) + else: + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + + def get_name(self): + """ + Retrieves the human-readable name of a sfp by 1-based index + + Returns: + :param index: An integer, 1-based index of the sfp of which to query status + :return: String, + A string representing the name of the sfp. + """ + return self._sfputil_helper.logical[self._sfp_index] or "Unknown" + + def get_presence(self): + """ + Retrieves the presence of the device + Returns: + bool: True if device is present, False if not + """ + return self._api_common.get_output(self._sfp_index, self._config['get_presence'], False) + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + + Returns: + string: Model/part number of device + """ + transceiver_dom_info_dict = self.get_transceiver_info() + return transceiver_dom_info_dict.get("model", Common.NULL_VAL) + + def get_serial(self): + """ + Retrieves the serial number of the device + + Returns: + string: Serial number of device + """ + transceiver_dom_info_dict = self.get_transceiver_info() + return transceiver_dom_info_dict.get("serial", Common.NULL_VAL) + + def get_status(self): + """ + Retrieves the operational status of the device + + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self.get_presence() + + def get_transceiver_info(self): + """ + Retrieves transceiver info of this SFP + Returns: + A dict which contains following keys/values : + ================================================================================ + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + type |1*255VCHAR |type of SFP + hardware_rev |1*255VCHAR |hardware version of SFP + serial |1*255VCHAR |serial number of the SFP + manufacturer |1*255VCHAR |SFP vendor name + model |1*255VCHAR |SFP model name + connector |1*255VCHAR |connector information + encoding |1*255VCHAR |encoding information + ext_identifier |1*255VCHAR |extend identifier + ext_rateselect_compliance |1*255VCHAR |extended rateSelect compliance + cable_length |INT |cable length in m + nominal_bit_rate |INT |nominal bit rate by 100Mbs + specification_compliance |1*255VCHAR |specification compliance + vendor_date |1*255VCHAR |vendor date + vendor_oui |1*255VCHAR |vendor OUI + application_advertisement |1*255VCHAR |supported applications advertisement + ================================================================================ + """ + + transceiver_info_dict = {} + compliance_code_dict = {} + transceiver_info_dict = dict.fromkeys( + info_dict_keys, Common.NULL_VAL) + + # ToDo: OSFP tranceiver info parsing not fully supported. + # in inf8628.py lack of some memory map definition + # will be implemented when the inf8628 memory map ready + if self.sfp_type == OSFP_TYPE: + offset = 0 + vendor_rev_width = XCVR_HW_REV_WIDTH_OSFP + + sfpi_obj = inf8628InterfaceId() + if sfpi_obj is None: + return None + + sfp_type_raw = self._read_eeprom_specific_bytes( + (offset + OSFP_TYPE_OFFSET), XCVR_TYPE_WIDTH) + if sfp_type_raw is not None: + sfp_type_data = sfpi_obj.parse_sfp_type(sfp_type_raw, 0) + else: + return None + + sfp_vendor_name_raw = self._read_eeprom_specific_bytes( + (offset + OSFP_VENDOR_NAME_OFFSET), XCVR_VENDOR_NAME_WIDTH) + if sfp_vendor_name_raw is not None: + sfp_vendor_name_data = sfpi_obj.parse_vendor_name( + sfp_vendor_name_raw, 0) + else: + return None + + sfp_vendor_pn_raw = self._read_eeprom_specific_bytes( + (offset + OSFP_VENDOR_PN_OFFSET), XCVR_VENDOR_PN_WIDTH) + if sfp_vendor_pn_raw is not None: + sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn( + sfp_vendor_pn_raw, 0) + else: + return None + + sfp_vendor_rev_raw = self._read_eeprom_specific_bytes( + (offset + OSFP_HW_REV_OFFSET), vendor_rev_width) + if sfp_vendor_rev_raw is not None: + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev( + sfp_vendor_rev_raw, 0) + else: + return None + + sfp_vendor_sn_raw = self._read_eeprom_specific_bytes( + (offset + OSFP_VENDOR_SN_OFFSET), XCVR_VENDOR_SN_WIDTH) + if sfp_vendor_sn_raw is not None: + sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn( + sfp_vendor_sn_raw, 0) + else: + return None + + transceiver_info_dict['type'] = sfp_type_data['data']['type']['value'] + transceiver_info_dict['manufacturer'] = sfp_vendor_name_data['data']['Vendor Name']['value'] + transceiver_info_dict['model'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] + transceiver_info_dict['hardware_rev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] + transceiver_info_dict['serial'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] + + elif self.sfp_type == QSFP_TYPE: + offset = 128 + vendor_rev_width = XCVR_HW_REV_WIDTH_QSFP + interface_info_bulk_width = XCVR_INTFACE_BULK_WIDTH_QSFP + + sfpi_obj = sff8436InterfaceId() + if sfpi_obj is None: + print("Error: sfp_object open failed") + return None + + elif self.sfp_type == QSFP_DD_TYPE: + offset = 128 + + sfpi_obj = qsfp_dd_InterfaceId() + if sfpi_obj is None: + print("Error: sfp_object open failed") + return None + + sfp_type_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_TYPE_OFFSET), XCVR_TYPE_WIDTH) + if sfp_type_raw is not None: + sfp_type_data = sfpi_obj.parse_sfp_type(sfp_type_raw, 0) + else: + return None + + sfp_vendor_name_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_VENDOR_NAME_OFFSET), XCVR_VENDOR_NAME_WIDTH) + if sfp_vendor_name_raw is not None: + sfp_vendor_name_data = sfpi_obj.parse_vendor_name( + sfp_vendor_name_raw, 0) + else: + return None + + sfp_vendor_pn_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_VENDOR_PN_OFFSET), XCVR_VENDOR_PN_WIDTH) + if sfp_vendor_pn_raw is not None: + sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn( + sfp_vendor_pn_raw, 0) + else: + return None + + sfp_vendor_rev_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_HW_REV_OFFSET_QSFP_DD), XCVR_HW_REV_WIDTH_QSFP_DD) + if sfp_vendor_rev_raw is not None: + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev( + sfp_vendor_rev_raw, 0) + else: + return None + + sfp_vendor_sn_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_VENDOR_SN_OFFSET), XCVR_VENDOR_SN_WIDTH) + if sfp_vendor_sn_raw is not None: + sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn( + sfp_vendor_sn_raw, 0) + else: + return None + + sfp_vendor_oui_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_VENDOR_OUI_OFFSET), XCVR_VENDOR_OUI_WIDTH) + if sfp_vendor_oui_raw is not None: + sfp_vendor_oui_data = sfpi_obj.parse_vendor_oui( + sfp_vendor_oui_raw, 0) + else: + return None + + sfp_vendor_date_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_VENDOR_DATE_OFFSET_QSFP_DD), XCVR_VENDOR_DATE_WIDTH_QSFP_DD) + if sfp_vendor_date_raw is not None: + sfp_vendor_date_data = sfpi_obj.parse_vendor_date( + sfp_vendor_date_raw, 0) + else: + return None + + sfp_connector_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_CONNECTOR_OFFSET_QSFP_DD), XCVR_CONNECTOR_WIDTH_QSFP_DD) + if sfp_connector_raw is not None: + sfp_connector_data = sfpi_obj.parse_connector( + sfp_connector_raw, 0) + else: + return None + + sfp_ext_identifier_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_EXT_TYPE_OFFSET_QSFP_DD), XCVR_EXT_TYPE_WIDTH_QSFP_DD) + if sfp_ext_identifier_raw is not None: + sfp_ext_identifier_data = sfpi_obj.parse_ext_iden( + sfp_ext_identifier_raw, 0) + else: + return None + + sfp_cable_len_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_CABLE_LENGTH_OFFSET_QSFP_DD), XCVR_CABLE_LENGTH_WIDTH_QSFP_DD) + if sfp_cable_len_raw is not None: + sfp_cable_len_data = sfpi_obj.parse_cable_len( + sfp_cable_len_raw, 0) + else: + return None + + sfp_media_type_raw = self._read_eeprom_specific_bytes( + XCVR_MEDIA_TYPE_OFFSET_QSFP_DD, XCVR_MEDIA_TYPE_WIDTH_QSFP_DD) + if sfp_media_type_raw is not None: + sfp_media_type_dict = sfpi_obj.parse_media_type( + sfp_media_type_raw, 0) + if sfp_media_type_dict is None: + return None + + host_media_list = "" + sfp_application_type_first_list = self._read_eeprom_specific_bytes( + (XCVR_FIRST_APPLICATION_LIST_OFFSET_QSFP_DD), XCVR_FIRST_APPLICATION_LIST_WIDTH_QSFP_DD) + if self.second_application_list: + possible_application_count = 15 + sfp_application_type_second_list = self._read_eeprom_specific_bytes( + (XCVR_SECOND_APPLICATION_LIST_OFFSET_QSFP_DD), XCVR_SECOND_APPLICATION_LIST_WIDTH_QSFP_DD) + if sfp_application_type_first_list is not None and sfp_application_type_second_list is not None: + sfp_application_type_list = sfp_application_type_first_list + \ + sfp_application_type_second_list + else: + return None + else: + possible_application_count = 8 + if sfp_application_type_first_list is not None: + sfp_application_type_list = sfp_application_type_first_list + else: + return None + + for i in range(0, possible_application_count): + if sfp_application_type_list[i * 4] == 'ff': + break + host_electrical, media_interface = sfpi_obj.parse_application( + sfp_media_type_dict, sfp_application_type_list[i * 4], sfp_application_type_list[i * 4 + 1]) + host_media_list = host_media_list + host_electrical + \ + ' - ' + media_interface + '\n\t\t\t\t ' + else: + return None + + transceiver_info_dict['type'] = str( + sfp_type_data['data']['type']['value']) + transceiver_info_dict['manufacturer'] = str( + sfp_vendor_name_data['data']['Vendor Name']['value']) + transceiver_info_dict['model'] = str( + sfp_vendor_pn_data['data']['Vendor PN']['value']) + transceiver_info_dict['hardware_rev'] = str( + sfp_vendor_rev_data['data']['Vendor Rev']['value']) + transceiver_info_dict['serial'] = str( + sfp_vendor_sn_data['data']['Vendor SN']['value']) + transceiver_info_dict['vendor_oui'] = str( + sfp_vendor_oui_data['data']['Vendor OUI']['value']) + transceiver_info_dict['vendor_date'] = str( + sfp_vendor_date_data['data']['VendorDataCode(YYYY-MM-DD Lot)']['value']) + transceiver_info_dict['connector'] = str( + sfp_connector_data['data']['Connector']['value']) + transceiver_info_dict['encoding'] = "Not supported for CMIS cables" + transceiver_info_dict['ext_identifier'] = str( + sfp_ext_identifier_data['data']['Extended Identifier']['value']) + transceiver_info_dict['ext_rateselect_compliance'] = "Not supported for CMIS cables" + transceiver_info_dict['specification_compliance'] = "Not supported for CMIS cables" + transceiver_info_dict['cable_type'] = "Length Cable Assembly(m)" + transceiver_info_dict['cable_length'] = str( + sfp_cable_len_data['data']['Length Cable Assembly(m)']['value']) + transceiver_info_dict['nominal_bit_rate'] = "Not supported for CMIS cables" + transceiver_info_dict['application_advertisement'] = host_media_list + + else: + offset = 0 + vendor_rev_width = XCVR_HW_REV_WIDTH_SFP + interface_info_bulk_width = XCVR_INTFACE_BULK_WIDTH_SFP + + sfpi_obj = sff8472InterfaceId() + if sfpi_obj is None: + print("Error: sfp_object open failed") + return None + + if self.sfp_type != QSFP_DD_TYPE: + sfp_interface_bulk_raw = self._read_eeprom_specific_bytes( + offset + XCVR_INTERFACE_DATA_START, XCVR_INTERFACE_DATA_SIZE) + if sfp_interface_bulk_raw is None: + return None + + start = XCVR_INTFACE_BULK_OFFSET - XCVR_INTERFACE_DATA_START + end = start + interface_info_bulk_width + sfp_interface_bulk_data = sfpi_obj.parse_sfp_info_bulk( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_NAME_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_NAME_WIDTH + sfp_vendor_name_data = sfpi_obj.parse_vendor_name( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_PN_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_PN_WIDTH + sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_HW_REV_OFFSET - XCVR_INTERFACE_DATA_START + end = start + vendor_rev_width + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_SN_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_SN_WIDTH + sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_OUI_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_OUI_WIDTH + sfp_vendor_oui_data = sfpi_obj.parse_vendor_oui( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_DATE_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_DATE_WIDTH + sfp_vendor_date_data = sfpi_obj.parse_vendor_date( + sfp_interface_bulk_raw[start: end], 0) + + transceiver_info_dict['type'] = sfp_interface_bulk_data['data']['type']['value'] + transceiver_info_dict['manufacturer'] = sfp_vendor_name_data['data']['Vendor Name']['value'] + transceiver_info_dict['model'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] + transceiver_info_dict['hardware_rev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] + transceiver_info_dict['serial'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] + transceiver_info_dict['vendor_oui'] = sfp_vendor_oui_data['data']['Vendor OUI']['value'] + transceiver_info_dict['vendor_date'] = sfp_vendor_date_data[ + 'data']['VendorDataCode(YYYY-MM-DD Lot)']['value'] + transceiver_info_dict['connector'] = sfp_interface_bulk_data['data']['Connector']['value'] + transceiver_info_dict['encoding'] = sfp_interface_bulk_data['data']['EncodingCodes']['value'] + transceiver_info_dict['ext_identifier'] = sfp_interface_bulk_data['data']['Extended Identifier']['value'] + transceiver_info_dict['ext_rateselect_compliance'] = sfp_interface_bulk_data['data']['RateIdentifier']['value'] + + if self.sfp_type == QSFP_TYPE: + for key in qsfp_cable_length_tup: + if key in sfp_interface_bulk_data['data']: + transceiver_info_dict['cable_type'] = key + transceiver_info_dict['cable_length'] = str( + sfp_interface_bulk_data['data'][key]['value']) + + for key in qsfp_compliance_code_tup: + if key in sfp_interface_bulk_data['data']['Specification compliance']['value']: + compliance_code_dict[key] = sfp_interface_bulk_data['data']['Specification compliance']['value'][key]['value'] + sfp_ext_specification_compliance_raw = self._read_eeprom_specific_bytes( + offset + XCVR_EXT_SPECIFICATION_COMPLIANCE_OFFSET, XCVR_EXT_SPECIFICATION_COMPLIANCE_WIDTH) + if sfp_ext_specification_compliance_raw is not None: + sfp_ext_specification_compliance_data = sfpi_obj.parse_ext_specification_compliance( + sfp_ext_specification_compliance_raw[0: 1], 0) + if sfp_ext_specification_compliance_data['data']['Extended Specification compliance']['value'] != "Unspecified": + compliance_code_dict['Extended Specification compliance'] = sfp_ext_specification_compliance_data[ + 'data']['Extended Specification compliance']['value'] + transceiver_info_dict['specification_compliance'] = str( + compliance_code_dict) + transceiver_info_dict['nominal_bit_rate'] = str( + sfp_interface_bulk_data['data']['Nominal Bit Rate(100Mbs)']['value']) + else: + for key in sfp_cable_length_tup: + if key in sfp_interface_bulk_data['data']: + transceiver_info_dict['cable_type'] = key + transceiver_info_dict['cable_length'] = str( + sfp_interface_bulk_data['data'][key]['value']) + + for key in sfp_compliance_code_tup: + if key in sfp_interface_bulk_data['data']['Specification compliance']['value']: + compliance_code_dict[key] = sfp_interface_bulk_data['data']['Specification compliance']['value'][key]['value'] + transceiver_info_dict['specification_compliance'] = str( + compliance_code_dict) + + transceiver_info_dict['nominal_bit_rate'] = str( + sfp_interface_bulk_data['data']['NominalSignallingRate(UnitsOf100Mbd)']['value']) + + return transceiver_info_dict + + def get_transceiver_bulk_status(self): + """ + Retrieves transceiver bulk status of this SFP + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + rx_los |BOOLEAN |RX loss-of-signal status, True if has RX los, False if not. + tx_fault |BOOLEAN |TX fault status, True if has TX fault, False if not. + reset_status |BOOLEAN |reset status, True if SFP in reset, False if not. + lp_mode |BOOLEAN |low power mode status, True in lp mode, False if not. + tx_disable |BOOLEAN |TX disable status, True TX disabled, False if not. + tx_disabled_channel |HEX |disabled TX channels in hex, bits 0 to 3 represent channel 0 + | |to channel 3. + temperature |INT |module temperature in Celsius + voltage |INT |supply voltage in mV + txbias |INT |TX Bias Current in mA, n is the channel number, + | |for example, tx2bias stands for tx bias of channel 2. + rxpower |INT |received optical power in mW, n is the channel number, + | |for example, rx2power stands for rx power of channel 2. + txpower |INT |TX output power in mW, n is the channel number, + | |for example, tx2power stands for tx power of channel 2. + ======================================================================== + """ + transceiver_dom_info_dict = dict.fromkeys( + dom_info_dict_keys, Common.NULL_VAL) + + if self.sfp_type == OSFP_TYPE: + pass + + elif self.sfp_type == QSFP_TYPE: + if not self.dom_supported: + return transceiver_dom_info_dict + + offset = 0 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return transceiver_dom_info_dict + + dom_data_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DOM_BULK_DATA_START), QSFP_DOM_BULK_DATA_SIZE) + if dom_data_raw is None: + return transceiver_dom_info_dict + + if self.dom_temp_supported: + start = QSFP_TEMPE_OFFSET - QSFP_DOM_BULK_DATA_START + end = start + QSFP_TEMPE_WIDTH + dom_temperature_data = sfpd_obj.parse_temperature( + dom_data_raw[start: end], 0) + temp = dom_temperature_data['data']['Temperature']['value'] + if temp is not None: + transceiver_dom_info_dict['temperature'] = temp + + if self.dom_volt_supported: + start = QSFP_VOLT_OFFSET - QSFP_DOM_BULK_DATA_START + end = start + QSFP_VOLT_WIDTH + dom_voltage_data = sfpd_obj.parse_voltage( + dom_data_raw[start: end], 0) + volt = dom_voltage_data['data']['Vcc']['value'] + if volt is not None: + transceiver_dom_info_dict['voltage'] = volt + + start = QSFP_CHANNL_MON_OFFSET - QSFP_DOM_BULK_DATA_START + end = start + QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power( + dom_data_raw[start: end], 0) + + if self.dom_tx_power_supported: + transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TX1Power']['value'] + transceiver_dom_info_dict['tx2power'] = dom_channel_monitor_data['data']['TX2Power']['value'] + transceiver_dom_info_dict['tx3power'] = dom_channel_monitor_data['data']['TX3Power']['value'] + transceiver_dom_info_dict['tx4power'] = dom_channel_monitor_data['data']['TX4Power']['value'] + + if self.dom_rx_power_supported: + transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RX1Power']['value'] + transceiver_dom_info_dict['rx2power'] = dom_channel_monitor_data['data']['RX2Power']['value'] + transceiver_dom_info_dict['rx3power'] = dom_channel_monitor_data['data']['RX3Power']['value'] + transceiver_dom_info_dict['rx4power'] = dom_channel_monitor_data['data']['RX4Power']['value'] + + transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TX1Bias']['value'] + transceiver_dom_info_dict['tx2bias'] = dom_channel_monitor_data['data']['TX2Bias']['value'] + transceiver_dom_info_dict['tx3bias'] = dom_channel_monitor_data['data']['TX3Bias']['value'] + transceiver_dom_info_dict['tx4bias'] = dom_channel_monitor_data['data']['TX4Bias']['value'] + + elif self.sfp_type == QSFP_DD_TYPE: + + offset = 0 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return transceiver_dom_info_dict + + dom_data_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_DOM_BULK_DATA_START), QSFP_DD_DOM_BULK_DATA_SIZE) + if dom_data_raw is None: + return transceiver_dom_info_dict + + if self.dom_temp_supported: + start = QSFP_DD_TEMPE_OFFSET - QSFP_DD_DOM_BULK_DATA_START + end = start + QSFP_DD_TEMPE_WIDTH + dom_temperature_data = sfpd_obj.parse_temperature( + dom_data_raw[start: end], 0) + temp = dom_temperature_data['data']['Temperature']['value'] + if temp is not None: + transceiver_dom_info_dict['temperature'] = temp + + if self.dom_volt_supported: + start = QSFP_DD_VOLT_OFFSET - QSFP_DD_DOM_BULK_DATA_START + end = start + QSFP_DD_VOLT_WIDTH + dom_voltage_data = sfpd_obj.parse_voltage( + dom_data_raw[start: end], 0) + volt = dom_voltage_data['data']['Vcc']['value'] + if volt is not None: + transceiver_dom_info_dict['voltage'] = volt + + if self.dom_rx_tx_power_bias_supported: + # page 11h + dom_data_raw = self._read_eeprom_specific_bytes( + (QSFP_DD_CHANNL_MON_OFFSET), QSFP_DD_CHANNL_MON_WIDTH) + if dom_data_raw is None: + return transceiver_dom_info_dict + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_data_raw, 0) + + if self.dom_tx_power_supported: + transceiver_dom_info_dict['tx1power'] = str( + dom_channel_monitor_data['data']['TX1Power']['value']) + transceiver_dom_info_dict['tx2power'] = str( + dom_channel_monitor_data['data']['TX2Power']['value']) + transceiver_dom_info_dict['tx3power'] = str( + dom_channel_monitor_data['data']['TX3Power']['value']) + transceiver_dom_info_dict['tx4power'] = str( + dom_channel_monitor_data['data']['TX4Power']['value']) + transceiver_dom_info_dict['tx5power'] = str( + dom_channel_monitor_data['data']['TX5Power']['value']) + transceiver_dom_info_dict['tx6power'] = str( + dom_channel_monitor_data['data']['TX6Power']['value']) + transceiver_dom_info_dict['tx7power'] = str( + dom_channel_monitor_data['data']['TX7Power']['value']) + transceiver_dom_info_dict['tx8power'] = str( + dom_channel_monitor_data['data']['TX8Power']['value']) + + if self.dom_rx_power_supported: + transceiver_dom_info_dict['rx1power'] = str( + dom_channel_monitor_data['data']['RX1Power']['value']) + transceiver_dom_info_dict['rx2power'] = str( + dom_channel_monitor_data['data']['RX2Power']['value']) + transceiver_dom_info_dict['rx3power'] = str( + dom_channel_monitor_data['data']['RX3Power']['value']) + transceiver_dom_info_dict['rx4power'] = str( + dom_channel_monitor_data['data']['RX4Power']['value']) + transceiver_dom_info_dict['rx5power'] = str( + dom_channel_monitor_data['data']['RX5Power']['value']) + transceiver_dom_info_dict['rx6power'] = str( + dom_channel_monitor_data['data']['RX6Power']['value']) + transceiver_dom_info_dict['rx7power'] = str( + dom_channel_monitor_data['data']['RX7Power']['value']) + transceiver_dom_info_dict['rx8power'] = str( + dom_channel_monitor_data['data']['RX8Power']['value']) + + if self.dom_tx_bias_power_supported: + transceiver_dom_info_dict['tx1bias'] = str( + dom_channel_monitor_data['data']['TX1Bias']['value']) + transceiver_dom_info_dict['tx2bias'] = str( + dom_channel_monitor_data['data']['TX2Bias']['value']) + transceiver_dom_info_dict['tx3bias'] = str( + dom_channel_monitor_data['data']['TX3Bias']['value']) + transceiver_dom_info_dict['tx4bias'] = str( + dom_channel_monitor_data['data']['TX4Bias']['value']) + transceiver_dom_info_dict['tx5bias'] = str( + dom_channel_monitor_data['data']['TX5Bias']['value']) + transceiver_dom_info_dict['tx6bias'] = str( + dom_channel_monitor_data['data']['TX6Bias']['value']) + transceiver_dom_info_dict['tx7bias'] = str( + dom_channel_monitor_data['data']['TX7Bias']['value']) + transceiver_dom_info_dict['tx8bias'] = str( + dom_channel_monitor_data['data']['TX8Bias']['value']) + + return transceiver_dom_info_dict + + else: + if not self.dom_supported: + return transceiver_dom_info_dict + + offset = 256 + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return transceiver_dom_info_dict + sfpd_obj._calibration_type = self.calibration + + dom_data_raw = self._read_eeprom_specific_bytes( + (offset + SFP_DOM_BULK_DATA_START), SFP_DOM_BULK_DATA_SIZE) + + start = SFP_TEMPE_OFFSET - SFP_DOM_BULK_DATA_START + end = start + SFP_TEMPE_WIDTH + dom_temperature_data = sfpd_obj.parse_temperature( + dom_data_raw[start: end], 0) + + start = SFP_VOLT_OFFSET - SFP_DOM_BULK_DATA_START + end = start + SFP_VOLT_WIDTH + dom_voltage_data = sfpd_obj.parse_voltage( + dom_data_raw[start: end], 0) + + start = SFP_CHANNL_MON_OFFSET - SFP_DOM_BULK_DATA_START + end = start + SFP_CHANNL_MON_WIDTH + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_data_raw[start: end], 0) + + transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value'] + transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value'] + transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RXPower']['value'] + transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TXBias']['value'] + transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TXPower']['value'] + + transceiver_dom_info_dict['lp_mode'] = self.get_lpmode() + transceiver_dom_info_dict['reset_status'] = self.get_reset_status() + transceiver_dom_info_dict['tx_disable'] = self.get_tx_disable() + transceiver_dom_info_dict['tx_disabled_channel'] = self.get_tx_disable_channel( + ) + + for key in transceiver_dom_info_dict: + val = transceiver_dom_info_dict[key] + transceiver_dom_info_dict[key] = self._convert_string_to_num( + val) if type(val) is str else val + + return transceiver_dom_info_dict + + def get_transceiver_threshold_info(self): + """ + Retrieves transceiver threshold info of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + temphighalarm |FLOAT |High Alarm Threshold value of temperature in Celsius. + templowalarm |FLOAT |Low Alarm Threshold value of temperature in Celsius. + temphighwarning |FLOAT |High Warning Threshold value of temperature in Celsius. + templowwarning |FLOAT |Low Warning Threshold value of temperature in Celsius. + vcchighalarm |FLOAT |High Alarm Threshold value of supply voltage in mV. + vcclowalarm |FLOAT |Low Alarm Threshold value of supply voltage in mV. + vcchighwarning |FLOAT |High Warning Threshold value of supply voltage in mV. + vcclowwarning |FLOAT |Low Warning Threshold value of supply voltage in mV. + rxpowerhighalarm |FLOAT |High Alarm Threshold value of received power in dBm. + rxpowerlowalarm |FLOAT |Low Alarm Threshold value of received power in dBm. + rxpowerhighwarning |FLOAT |High Warning Threshold value of received power in dBm. + rxpowerlowwarning |FLOAT |Low Warning Threshold value of received power in dBm. + txpowerhighalarm |FLOAT |High Alarm Threshold value of transmit power in dBm. + txpowerlowalarm |FLOAT |Low Alarm Threshold value of transmit power in dBm. + txpowerhighwarning |FLOAT |High Warning Threshold value of transmit power in dBm. + txpowerlowwarning |FLOAT |Low Warning Threshold value of transmit power in dBm. + txbiashighalarm |FLOAT |High Alarm Threshold value of tx Bias Current in mA. + txbiaslowalarm |FLOAT |Low Alarm Threshold value of tx Bias Current in mA. + txbiashighwarning |FLOAT |High Warning Threshold value of tx Bias Current in mA. + txbiaslowwarning |FLOAT |Low Warning Threshold value of tx Bias Current in mA. + ======================================================================== + """ + transceiver_dom_threshold_info_dict = dict.fromkeys( + threshold_dict_keys, Common.NULL_VAL) + + if self.sfp_type == OSFP_TYPE: + pass + + elif self.sfp_type == QSFP_TYPE: + if not self.dom_supported or not self.qsfp_page3_available: + return transceiver_dom_threshold_info_dict + + # Dom Threshold data starts from offset 384 + # Revert offset back to 0 once data is retrieved + offset = QSFP_MODULE_UPPER_PAGE3_START + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return transceiver_dom_threshold_info_dict + + dom_module_threshold_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_MODULE_THRESHOLD_OFFSET), QSFP_MODULE_THRESHOLD_WIDTH) + if dom_module_threshold_raw is None: + return transceiver_dom_threshold_info_dict + + dom_module_threshold_data = sfpd_obj.parse_module_threshold_values( + dom_module_threshold_raw, 0) + + dom_channel_threshold_raw = self._read_eeprom_specific_bytes((offset + QSFP_CHANNL_THRESHOLD_OFFSET), + QSFP_CHANNL_THRESHOLD_WIDTH) + if dom_channel_threshold_raw is None: + return transceiver_dom_threshold_info_dict + dom_channel_threshold_data = sfpd_obj.parse_channel_threshold_values( + dom_channel_threshold_raw, 0) + + # Threshold Data + transceiver_dom_threshold_info_dict['temphighalarm'] = dom_module_threshold_data['data']['TempHighAlarm']['value'] + transceiver_dom_threshold_info_dict['temphighwarning'] = dom_module_threshold_data['data']['TempHighWarning']['value'] + transceiver_dom_threshold_info_dict['templowalarm'] = dom_module_threshold_data['data']['TempLowAlarm']['value'] + transceiver_dom_threshold_info_dict['templowwarning'] = dom_module_threshold_data['data']['TempLowWarning']['value'] + transceiver_dom_threshold_info_dict['vcchighalarm'] = dom_module_threshold_data['data']['VccHighAlarm']['value'] + transceiver_dom_threshold_info_dict['vcchighwarning'] = dom_module_threshold_data['data']['VccHighWarning']['value'] + transceiver_dom_threshold_info_dict['vcclowalarm'] = dom_module_threshold_data['data']['VccLowAlarm']['value'] + transceiver_dom_threshold_info_dict['vcclowwarning'] = dom_module_threshold_data['data']['VccLowWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighalarm'] = dom_channel_threshold_data['data']['RxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighwarning'] = dom_channel_threshold_data['data']['RxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowalarm'] = dom_channel_threshold_data['data']['RxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowwarning'] = dom_channel_threshold_data['data']['RxPowerLowWarning']['value'] + transceiver_dom_threshold_info_dict['txbiashighalarm'] = dom_channel_threshold_data['data']['TxBiasHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiashighwarning'] = dom_channel_threshold_data['data']['TxBiasHighWarning']['value'] + transceiver_dom_threshold_info_dict['txbiaslowalarm'] = dom_channel_threshold_data['data']['TxBiasLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiaslowwarning'] = dom_channel_threshold_data['data']['TxBiasLowWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerhighalarm'] = dom_channel_threshold_data['data']['TxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerhighwarning'] = dom_channel_threshold_data['data']['TxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerlowalarm'] = dom_channel_threshold_data['data']['TxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerlowwarning'] = dom_channel_threshold_data['data']['TxPowerLowWarning']['value'] + + elif self.sfp_type == QSFP_DD_TYPE: + if not self.dom_supported: + return transceiver_dom_threshold_info_dict + + if not self.dom_thresholds_supported: + return transceiver_dom_threshold_info_dict + + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return transceiver_dom_threshold_info_dict + + # page 02 + offset = 384 + dom_module_threshold_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_MODULE_THRESHOLD_OFFSET), QSFP_DD_MODULE_THRESHOLD_WIDTH) + if dom_module_threshold_raw is None: + return transceiver_dom_threshold_info_dict + + dom_module_threshold_data = sfpd_obj.parse_module_threshold_values( + dom_module_threshold_raw, 0) + + # Threshold Data + transceiver_dom_threshold_info_dict['temphighalarm'] = dom_module_threshold_data['data']['TempHighAlarm']['value'] + transceiver_dom_threshold_info_dict['temphighwarning'] = dom_module_threshold_data['data']['TempHighWarning']['value'] + transceiver_dom_threshold_info_dict['templowalarm'] = dom_module_threshold_data['data']['TempLowAlarm']['value'] + transceiver_dom_threshold_info_dict['templowwarning'] = dom_module_threshold_data['data']['TempLowWarning']['value'] + transceiver_dom_threshold_info_dict['vcchighalarm'] = dom_module_threshold_data['data']['VccHighAlarm']['value'] + transceiver_dom_threshold_info_dict['vcchighwarning'] = dom_module_threshold_data['data']['VccHighWarning']['value'] + transceiver_dom_threshold_info_dict['vcclowalarm'] = dom_module_threshold_data['data']['VccLowAlarm']['value'] + transceiver_dom_threshold_info_dict['vcclowwarning'] = dom_module_threshold_data['data']['VccLowWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighalarm'] = dom_module_threshold_data['data']['RxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighwarning'] = dom_module_threshold_data['data']['RxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowalarm'] = dom_module_threshold_data['data']['RxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowwarning'] = dom_module_threshold_data['data']['RxPowerLowWarning']['value'] + transceiver_dom_threshold_info_dict['txbiashighalarm'] = dom_module_threshold_data['data']['TxBiasHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiashighwarning'] = dom_module_threshold_data['data']['TxBiasHighWarning']['value'] + transceiver_dom_threshold_info_dict['txbiaslowalarm'] = dom_module_threshold_data['data']['TxBiasLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiaslowwarning'] = dom_module_threshold_data['data']['TxBiasLowWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerhighalarm'] = dom_module_threshold_data['data']['TxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerhighwarning'] = dom_module_threshold_data['data']['TxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerlowalarm'] = dom_module_threshold_data['data']['TxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerlowwarning'] = dom_module_threshold_data['data']['TxPowerLowWarning']['value'] + + else: + offset = SFP_MODULE_ADDRA2_OFFSET + + if not self.dom_supported: + return transceiver_dom_threshold_info_dict + + sfpd_obj = sff8472Dom(None, self.calibration) + if sfpd_obj is None: + return transceiver_dom_threshold_info_dict + + dom_module_threshold_raw = self._read_eeprom_specific_bytes((offset + SFP_MODULE_THRESHOLD_OFFSET), + SFP_MODULE_THRESHOLD_WIDTH) + if dom_module_threshold_raw is not None: + dom_module_threshold_data = sfpd_obj.parse_alarm_warning_threshold( + dom_module_threshold_raw, 0) + else: + return transceiver_dom_threshold_info_dict + + # Threshold Data + transceiver_dom_threshold_info_dict['temphighalarm'] = dom_module_threshold_data['data']['TempHighAlarm']['value'] + transceiver_dom_threshold_info_dict['templowalarm'] = dom_module_threshold_data['data']['TempLowAlarm']['value'] + transceiver_dom_threshold_info_dict['temphighwarning'] = dom_module_threshold_data['data']['TempHighWarning']['value'] + transceiver_dom_threshold_info_dict['templowwarning'] = dom_module_threshold_data['data']['TempLowWarning']['value'] + transceiver_dom_threshold_info_dict['vcchighalarm'] = dom_module_threshold_data['data']['VoltageHighAlarm']['value'] + transceiver_dom_threshold_info_dict['vcclowalarm'] = dom_module_threshold_data['data']['VoltageLowAlarm']['value'] + transceiver_dom_threshold_info_dict['vcchighwarning'] = dom_module_threshold_data[ + 'data']['VoltageHighWarning']['value'] + transceiver_dom_threshold_info_dict['vcclowwarning'] = dom_module_threshold_data['data']['VoltageLowWarning']['value'] + transceiver_dom_threshold_info_dict['txbiashighalarm'] = dom_module_threshold_data['data']['BiasHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiaslowalarm'] = dom_module_threshold_data['data']['BiasLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiashighwarning'] = dom_module_threshold_data['data']['BiasHighWarning']['value'] + transceiver_dom_threshold_info_dict['txbiaslowwarning'] = dom_module_threshold_data['data']['BiasLowWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerhighalarm'] = dom_module_threshold_data['data']['TXPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerlowalarm'] = dom_module_threshold_data['data']['TXPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerhighwarning'] = dom_module_threshold_data['data']['TXPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerlowwarning'] = dom_module_threshold_data['data']['TXPowerLowWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighalarm'] = dom_module_threshold_data['data']['RXPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowalarm'] = dom_module_threshold_data['data']['RXPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighwarning'] = dom_module_threshold_data['data']['RXPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowwarning'] = dom_module_threshold_data['data']['RXPowerLowWarning']['value'] + + for key in transceiver_dom_threshold_info_dict: + transceiver_dom_threshold_info_dict[key] = self._convert_string_to_num( + transceiver_dom_threshold_info_dict[key]) + + return transceiver_dom_threshold_info_dict + + def get_reset_status(self): + """ + Retrieves the reset status of SFP + Returns: + A Boolean, True if reset enabled, False if disabled + """ + return self._api_common.get_output(self._sfp_index, self._config['get_reset_status'], False) + + def get_rx_los(self): + """ + Retrieves the RX LOS (lost-of-signal) status of SFP + Returns: + A Boolean, True if SFP has RX LOS, False if not. + Note : RX LOS status is latched until a call to get_rx_los or a reset. + """ + if not self.dom_supported: + return None + + rx_los_list = [] + if self.sfp_type == OSFP_TYPE: + return None + elif self.sfp_type == QSFP_TYPE: + offset = 0 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_RX_LOS_STATUS_OFFSET), QSFP_CHANNL_RX_LOS_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + rx_los_data = int(dom_channel_monitor_raw[0], 16) + rx_los_list.append(rx_los_data & 0x01 != 0) + rx_los_list.append(rx_los_data & 0x02 != 0) + rx_los_list.append(rx_los_data & 0x04 != 0) + rx_los_list.append(rx_los_data & 0x08 != 0) + + elif self.sfp_type == QSFP_DD_TYPE: + # page 11h + if self.dom_rx_tx_power_bias_supported: + offset = 128 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_CHANNL_RX_LOS_STATUS_OFFSET), QSFP_DD_CHANNL_RX_LOS_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + rx_los_data = int(dom_channel_monitor_raw[0], 8) + rx_los_list.append(rx_los_data & 0x01 != 0) + rx_los_list.append(rx_los_data & 0x02 != 0) + rx_los_list.append(rx_los_data & 0x04 != 0) + rx_los_list.append(rx_los_data & 0x08 != 0) + rx_los_list.append(rx_los_data & 0x10 != 0) + rx_los_list.append(rx_los_data & 0x20 != 0) + rx_los_list.append(rx_los_data & 0x40 != 0) + rx_los_list.append(rx_los_data & 0x80 != 0) + + else: + offset = 256 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + SFP_CHANNL_STATUS_OFFSET), SFP_CHANNL_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + rx_los_data = int(dom_channel_monitor_raw[0], 16) + rx_los_list.append(rx_los_data & 0x02 != 0) + else: + return None + return rx_los_list + + def get_tx_fault(self): + """ + Retrieves the TX fault status of SFP + Returns: + A Boolean, True if SFP has TX fault, False if not + Note : TX fault status is lached until a call to get_tx_fault or a reset. + """ + if not self.dom_supported: + return None + + tx_fault_list = [] + if self.sfp_type == OSFP_TYPE: + return None + elif self.sfp_type == QSFP_TYPE: + offset = 0 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_TX_FAULT_STATUS_OFFSET), QSFP_CHANNL_TX_FAULT_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_fault_data = int(dom_channel_monitor_raw[0], 16) + tx_fault_list.append(tx_fault_data & 0x01 != 0) + tx_fault_list.append(tx_fault_data & 0x02 != 0) + tx_fault_list.append(tx_fault_data & 0x04 != 0) + tx_fault_list.append(tx_fault_data & 0x08 != 0) + + elif self.sfp_type == QSFP_DD_TYPE: + # page 11h + if self.dom_rx_tx_power_bias_supported: + offset = 128 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_CHANNL_TX_FAULT_STATUS_OFFSET), QSFP_DD_CHANNL_TX_FAULT_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_fault_data = int(dom_channel_monitor_raw[0], 8) + tx_fault_list.append(tx_fault_data & 0x01 != 0) + tx_fault_list.append(tx_fault_data & 0x02 != 0) + tx_fault_list.append(tx_fault_data & 0x04 != 0) + tx_fault_list.append(tx_fault_data & 0x08 != 0) + tx_fault_list.append(tx_fault_data & 0x10 != 0) + tx_fault_list.append(tx_fault_data & 0x20 != 0) + tx_fault_list.append(tx_fault_data & 0x40 != 0) + tx_fault_list.append(tx_fault_data & 0x80 != 0) + + else: + offset = 256 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + SFP_CHANNL_STATUS_OFFSET), SFP_CHANNL_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_fault_data = int(dom_channel_monitor_raw[0], 16) + tx_fault_list.append(tx_fault_data & 0x04 != 0) + else: + return None + return tx_fault_list + + def get_tx_disable(self): + """ + Retrieves the tx_disable status of this SFP + Returns: + A list of boolean values, representing the TX disable status + of each available channel, value is True if SFP channel + is TX disabled, False if not. + E.g., for a tranceiver with four channels: [False, False, True, False] + """ + if not self.dom_supported: + return None + + tx_disable_list = [] + if self.sfp_type == OSFP_TYPE: + return None + elif self.sfp_type == QSFP_TYPE: + offset = 0 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_DISABLE_STATUS_OFFSET), QSFP_CHANNL_DISABLE_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_disable_data = int(dom_channel_monitor_raw[0], 16) + tx_disable_list.append(tx_disable_data & 0x01 != 0) + tx_disable_list.append(tx_disable_data & 0x02 != 0) + tx_disable_list.append(tx_disable_data & 0x04 != 0) + tx_disable_list.append(tx_disable_data & 0x08 != 0) + + elif self.sfp_type == QSFP_DD_TYPE: + if self.dom_rx_tx_power_bias_supported: + offset = 128 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_CHANNL_DISABLE_STATUS_OFFSET), QSFP_DD_CHANNL_DISABLE_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_disable_data = int(dom_channel_monitor_raw[0], 16) + tx_disable_list.append(tx_disable_data & 0x01 != 0) + tx_disable_list.append(tx_disable_data & 0x02 != 0) + tx_disable_list.append(tx_disable_data & 0x04 != 0) + tx_disable_list.append(tx_disable_data & 0x08 != 0) + tx_disable_list.append(tx_disable_data & 0x10 != 0) + tx_disable_list.append(tx_disable_data & 0x20 != 0) + tx_disable_list.append(tx_disable_data & 0x40 != 0) + tx_disable_list.append(tx_disable_data & 0x80 != 0) + + else: + offset = 256 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + SFP_CHANNL_STATUS_OFFSET), SFP_CHANNL_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_disable_data = int(dom_channel_monitor_raw[0], 16) + tx_disable_list.append(tx_disable_data & 0xC0 != 0) + else: + return None + return tx_disable_list + + def get_tx_disable_channel(self): + """ + Retrieves the TX disabled channels in this SFP + Returns: + A hex of 4 bits (bit 0 to bit 3 as channel 0 to channel 3) to represent + TX channels which have been disabled in this SFP. + As an example, a returned value of 0x5 indicates that channel 0 + and channel 2 have been disabled. + """ + tx_disable_list = self.get_tx_disable() + if tx_disable_list is None: + return 0 + tx_disabled = 0 + for i in range(len(tx_disable_list)): + if tx_disable_list[i]: + tx_disabled |= 1 << i + return tx_disabled + + def get_lpmode(self): + """ + Retrieves the lpmode (low power mode) status of this SFP + Returns: + A Boolean, True if lpmode is enabled, False if disabled + """ + return self._api_common.get_output(self._sfp_index, self._config['get_lpmode'], False) + + def get_power_override(self): + """ + Retrieves the power-override status of this SFP + Returns: + A Boolean, True if power-override is enabled, False if disabled + """ + if self.sfp_type == QSFP_TYPE: + offset = 0 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return False + + dom_control_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CONTROL_OFFSET), QSFP_CONTROL_WIDTH) + if dom_control_raw is not None: + dom_control_data = sfpd_obj.parse_control_bytes( + dom_control_raw, 0) + return ('On' == dom_control_data['data']['PowerOverride']) + else: + return NotImplementedError + + def get_temperature(self): + """ + Retrieves the temperature of this SFP + Returns: + An integer number of current temperature in Celsius + """ + if not self.dom_supported: + return None + if self.sfp_type == QSFP_TYPE: + offset = 0 + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return None + + if self.dom_temp_supported: + dom_temperature_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_TEMPE_OFFSET), QSFP_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature( + dom_temperature_raw, 0) + temp = self._convert_string_to_num( + dom_temperature_data['data']['Temperature']['value']) + return temp + else: + return None + else: + return None + + elif self.sfp_type == QSFP_DD_TYPE: + offset = 0 + + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return None + + if self.dom_temp_supported: + dom_temperature_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_TEMPE_OFFSET), QSFP_DD_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature( + dom_temperature_raw, 0) + temp = self._convert_string_to_num( + dom_temperature_data['data']['Temperature']['value']) + return temp + return None + + else: + offset = 256 + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return None + sfpd_obj._calibration_type = 1 + + dom_temperature_raw = self._read_eeprom_specific_bytes( + (offset + SFP_TEMPE_OFFSET), SFP_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature( + dom_temperature_raw, 0) + temp = self._convert_string_to_num( + dom_temperature_data['data']['Temperature']['value']) + return temp + else: + return None + + def get_voltage(self): + """ + Retrieves the supply voltage of this SFP + Returns: + An integer number of supply voltage in mV + """ + if not self.dom_supported: + return None + if self.sfp_type == QSFP_TYPE: + offset = 0 + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return None + + if self.dom_volt_supported: + dom_voltage_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_VOLT_OFFSET), QSFP_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage( + dom_voltage_raw, 0) + voltage = self._convert_string_to_num( + dom_voltage_data['data']['Vcc']['value']) + return voltage + else: + return None + return None + + if self.sfp_type == QSFP_DD_TYPE: + offset = 128 + + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return None + + if self.dom_volt_supported: + dom_voltage_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_VOLT_OFFSET), QSFP_DD_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage( + dom_voltage_raw, 0) + voltage = self._convert_string_to_num( + dom_voltage_data['data']['Vcc']['value']) + return voltage + return None + + else: + offset = 256 + + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return None + + sfpd_obj._calibration_type = self.calibration + + dom_voltage_raw = self._read_eeprom_specific_bytes( + (offset + SFP_VOLT_OFFSET), SFP_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0) + voltage = self._convert_string_to_num( + dom_voltage_data['data']['Vcc']['value']) + return voltage + else: + return None + + def get_tx_bias(self): + """ + Retrieves the TX bias current of this SFP + Returns: + A list of four integer numbers, representing TX bias in mA + for channel 0 to channel 4. + Ex. ['110.09', '111.12', '108.21', '112.09'] + """ + tx_bias_list = [] + if self.sfp_type == QSFP_TYPE: + offset = 0 + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return None + + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power( + dom_channel_monitor_raw, 0) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX1Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX2Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX3Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX4Bias']['value'])) + + elif self.sfp_type == QSFP_DD_TYPE: + # page 11h + if self.dom_rx_tx_power_bias_supported: + offset = 128 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return None + + if dom_tx_bias_power_supported: + dom_tx_bias_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_TX_BIAS_OFFSET), QSFP_DD_TX_BIAS_WIDTH) + if dom_tx_bias_raw is not None: + dom_tx_bias_data = sfpd_obj.parse_dom_tx_bias( + dom_tx_bias_raw, 0) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX1Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX2Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX3Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX4Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX5Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX6Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX7Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX8Bias']['value'])) + + else: + offset = 256 + + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return None + sfpd_obj._calibration_type = self.calibration + + if self.dom_supported: + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_channel_monitor_raw, 0) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TXBias']['value'])) + else: + return None + else: + return None + + return tx_bias_list + + def get_rx_power(self): + """ + Retrieves the received optical power for this SFP + Returns: + A list of four integer numbers, representing received optical + power in mW for channel 0 to channel 4. + Ex. ['1.77', '1.71', '1.68', '1.70'] + """ + rx_power_list = [] + if self.sfp_type == OSFP_TYPE: + # OSFP not supported on our platform yet. + return None + + elif self.sfp_type == QSFP_TYPE: + offset = 0 + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return None + + if self.dom_rx_power_supported: + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power( + dom_channel_monitor_raw, 0) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RX1Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RX2Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RX3Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RX4Power']['value'])) + else: + return None + else: + return None + + elif self.sfp_type == QSFP_DD_TYPE: + # page 11 + if self.dom_rx_tx_power_bias_supported: + offset = 128 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return None + + if self.dom_rx_power_supported: + dom_rx_power_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_RX_POWER_OFFSET), QSFP_DD_RX_POWER_WIDTH) + if dom_rx_power_raw is not None: + dom_rx_power_data = sfpd_obj.parse_dom_rx_power( + dom_rx_power_raw, 0) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX1Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX2Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX3Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX4Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX5Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX6Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX7Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX8Power']['value'])) + + else: + offset = 256 + + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return None + + if self.dom_supported: + sfpd_obj._calibration_type = self.calibration + + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_channel_monitor_raw, 0) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RXPower']['value'])) + else: + return None + else: + return None + return rx_power_list + + def get_tx_power(self): + """ + Retrieves the TX power of this SFP + Returns: + A list of four integer numbers, representing TX power in mW + for channel 0 to channel 4. + Ex. ['1.86', '1.86', '1.86', '1.86'] + """ + tx_power_list = [] + if self.sfp_type == OSFP_TYPE: + # OSFP not supported on our platform yet. + return None + + elif self.sfp_type == QSFP_TYPE: + offset = 0 + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return None + + if self.dom_tx_power_supported: + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power( + dom_channel_monitor_raw, 0) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX1Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX2Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX3Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX4Power']['value'])) + else: + return None + else: + return None + + elif self.sfp_type == QSFP_DD_TYPE: + # page 11 + if self.dom_rx_tx_power_bias_supported: + offset = 128 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return None + + if self.dom_tx_power_supported: + dom_tx_power_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_TX_POWER_OFFSET), QSFP_DD_TX_POWER_WIDTH) + if dom_tx_power_raw is not None: + dom_tx_power_data = sfpd_obj.parse_dom_tx_power( + dom_tx_power_raw, 0) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX1Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX2Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX3Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX4Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX5Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX6Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX7Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX8Power']['value'])) + + else: + offset = 256 + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return None + + if self.dom_supported: + sfpd_obj._calibration_type = self.calibration + + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_channel_monitor_raw, 0) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TXPower']['value'])) + else: + return None + else: + return None + return tx_power_list + + def reset(self): + """ + Reset SFP and return all user module settings to their default srate. + + Returns: + A boolean, True if successful, False if not + """ + config = self._config['reset'] + # Convert our register value back to a hex string and write back + output1 = self._api_common.set_output(self._sfp_index, "0x0", config) + + # Sleep 1 second to allow it to settle + time.sleep(1) + # Flip the bit back high and write back to the register to take port out of reset + output2 = self._api_common.set_output(self._sfp_index, "0x1", config) + + return True if (output1 and output2) else False + + def tx_disable(self, tx_disable): + """ + Disable SFP TX for all channels + + Args: + tx_disable : A Boolean, True to enable tx_disable mode, False to disable + tx_disable mode. + + Returns: + A boolean, True if tx_disable is set successfully, False if not + """ + if self.sfp_type == QSFP_TYPE: + sysfsfile_eeprom = None + try: + tx_disable_ctl = 0xf if tx_disable else 0x0 + buffer = create_string_buffer(1) + buffer[0] = chr(tx_disable_ctl) + # Write to eeprom + sysfs_sfp_i2c_client_eeprom_path = self._get_eeprom_path() + sysfsfile_eeprom = open( + sysfs_sfp_i2c_client_eeprom_path, "r+b") + sysfsfile_eeprom.seek(QSFP_CONTROL_OFFSET) + sysfsfile_eeprom.write(buffer[0]) + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + finally: + if sysfsfile_eeprom is not None: + sysfsfile_eeprom.close() + time.sleep(0.01) + return True + return False + + def tx_disable_channel(self, channel, disable): + """ + Sets the tx_disable for specified SFP channels + + Args: + channel : A hex of 4 bits (bit 0 to bit 3) which represent channel 0 to 3, + e.g. 0x5 for channel 0 and channel 2. + disable : A boolean, True to disable TX channels specified in channel, + False to enable + + Returns: + A boolean, True if successful, False if not + """ + if self.sfp_type == QSFP_TYPE: + sysfsfile_eeprom = None + try: + channel_state = self.get_tx_disable_channel() + tx_enable_mask = [0xe, 0xd, 0xb, 0x7] + tx_disable_mask = [0x1, 0x3, 0x7, 0xf] + tx_disable_ctl = channel_state | tx_disable_mask[ + channel] if disable else channel_state & tx_enable_mask[channel] + buffer = create_string_buffer(1) + buffer[0] = chr(tx_disable_ctl) + # Write to eeprom + sysfs_sfp_i2c_client_eeprom_path = self._get_eeprom_path() + sysfsfile_eeprom = open( + sysfs_sfp_i2c_client_eeprom_path, "r+b") + sysfsfile_eeprom.seek(QSFP_CONTROL_OFFSET) + sysfsfile_eeprom.write(buffer[0]) + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + finally: + if sysfsfile_eeprom is not None: + sysfsfile_eeprom.close() + time.sleep(0.01) + return True + return False + + def set_lpmode(self, lpmode): + """ + Sets the lpmode (low power mode) of SFP + + Args: + lpmode: A Boolean, True to enable lpmode, False to disable it + Note : lpmode can be overridden by set_power_override + + Returns: + A boolean, True if lpmode is set successfully, False if not + """ + return self._api_common.set_output(self._sfp_index, str(lpmode), self._config['set_lpmode']) + + def set_power_override(self, power_override, power_set): + """ + Sets SFP power level using power_override and power_set + + Args: + power_override : + A Boolean, True to override set_lpmode and use power_set + to control SFP power, False to disable SFP power control + through power_override/power_set and use set_lpmode + to control SFP power. + power_set : + Only valid when power_override is True. + A Boolean, True to set SFP to low power mode, False to set + SFP to high power mode. + + Returns: + A boolean, True if power-override and power_set are set successfully, + False if not + """ + if self.sfp_type == QSFP_TYPE: + sysfsfile_eeprom = None + try: + power_override_bit = 0 + if power_override: + power_override_bit |= 1 << 0 + + power_set_bit = 0 + if power_set: + power_set_bit |= 1 << 1 + + buffer = create_string_buffer(1) + buffer[0] = chr(power_override_bit | power_set_bit) + # Write to eeprom + sysfs_sfp_i2c_client_eeprom_path = self._get_eeprom_path() + sysfsfile_eeprom = open( + sysfs_sfp_i2c_client_eeprom_path, "r+b") + sysfsfile_eeprom.seek(QSFP_POWEROVERRIDE_OFFSET) + sysfsfile_eeprom.write(buffer[0]) + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + finally: + if sysfsfile_eeprom is not None: + sysfsfile_eeprom.close() + time.sleep(0.01) + return True + + return False diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/thermal.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/thermal.py new file mode 100644 index 000000000000..714e073db37d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/sonic_platform/thermal.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Module contains an implementation of SONiC Platform Base API and +# provides the thermal status which are available in the platform +# +############################################################################# + +try: + from sonic_platform_base.thermal_base import ThermalBase + from common import Common +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Thermal(ThermalBase): + """Platform-specific Thermal class""" + + def __init__(self, index, _thermal_index=0, conf=None): + ThermalBase.__init__(self) + + self._thermal_index = index + self._config = conf + self._api_common = Common(self._config) + + def get_name(self): + """ + Retrieves the human-readable name of a thermal sensor by 1-based index + + Returns: + :param index: An integer, 1-based index of the thermal sensor of which to query status + :return: String, + A string representing the name of the thermal sensor. + """ + return self._api_common.get_output(self._thermal_index, self._config['get_name'], Common.NULL_VAL) + + def get_temperature(self): + """ + Retrieves current temperature reading from thermal + + by using command ipmitool raw 0x04 0x2D [address] + + Returns: + A float number of current temperature in Celsius up to nearest thousandth + of one degree Celsius, e.g. 30.125 + """ + output = self._api_common.get_output( + self._thermal_index, self._config['get_temperature'], Common.NULL_VAL) + return float(output) if output != Common.NULL_VAL else output + + def get_high_threshold(self): + """ + Retrieves the high threshold temperature of thermal + Returns: + A float number, the high threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + output = self._api_common.get_output( + self._thermal_index, self._config['get_high_threshold'], Common.NULL_VAL) + return float(output) if output != Common.NULL_VAL else output + + def get_low_threshold(self): + """ + Retrieves the low threshold temperature of thermal + + Returns: + lnc as float number and return 0 if the BMC output is na. + A float number, the low threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + output = self._api_common.get_output( + self._thermal_index, self._config['get_low_threshold'], Common.NULL_VAL) + return float(output) if output != Common.NULL_VAL else output + + def set_high_threshold(self, temperature): + """ + Sets the high threshold temperature of thermal + + For AMI BMC device : + use ipmitool command + ipmitool sensor thresh [sensor name] unc [0>= temp_value <=62] + if the current value of unc is 'na' ipmitool can't be set the value + + Args : + temperature: A float number up to nearest thousandth of one degree Celsius, + e.g. 30.125 + + Returns: + A boolean, True if threshold is set successfully, False if not + """ + output = self._api_common.get_output( + self._thermal_index, self._config['set_high_threshold'], Common.NULL_VAL) + return float(output) if output != Common.NULL_VAL else output + + def set_low_threshold(self, temperature): + """ + Sets the low threshold temperature of thermal + + For AMI BMC device : + use ipmitool command + ipmitool sensor thresh [sensor name] lnc [temp_value] + if the current value of lnc is 'na' ipmitool can't be set the value + + Args : + temperature: A float number up to nearest thousandth of one degree Celsius, + e.g. 30.125 + + Returns: + A boolean, True if threshold is set successfully, False if not + """ + output = self._api_common.get_output( + self._thermal_index, self._config['set_low_threshold'], Common.NULL_VAL) + return float(output) if output != Common.NULL_VAL else output + + def get_high_critical_threshold(self): + """ + Retrieves the high critical threshold temperature of thermal + + Returns: + ucr as float number and return 0 if the BMC output is na. + A float number, the high critical threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + output = self._api_common.get_output( + self._thermal_index, self._config['get_high_critical_threshold'], Common.NULL_VAL) + return float(output) if output != Common.NULL_VAL else output + + def get_low_critical_threshold(self): + """ + Retrieves the low critical threshold temperature of thermal + + Returns: + lnr as float number and return 0 if the BMC output is na. + A float number, the low critical threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + output = self._api_common.get_output( + self._thermal_index, self._config['get_low_critical_threshold'], Common.NULL_VAL) + return float(output) if output != Common.NULL_VAL else output + + def get_presence(self): + """ + Retrieves the presence of the device + Returns: + bool: True if device is present, False if not + """ + return True + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + + Returns: + string: Model/part number of device + """ + return Common.NULL_VAL + + def get_serial(self): + """ + Retrieves the serial number of the device + + Returns: + string: Serial number of device + """ + return Common.NULL_VAL + + def get_status(self): + """ + Retrieves the operational status of the device + + Returns: + A boolean value, True if device is operating properly, False if not + """ + return True From 2929c809964233c4fd4798dbd1581bcce84ef2ed Mon Sep 17 00:00:00 2001 From: Kantaphon Kovathana Date: Thu, 18 Mar 2021 17:22:24 +0700 Subject: [PATCH 4/4] [platform/cel] fix debian rule and move cel seatone2 to its group --- platform/broadcom/one-image.mk | 4 ++-- .../broadcom/sonic-platform-modules-cel/debian/rules | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/platform/broadcom/one-image.mk b/platform/broadcom/one-image.mk index 2b53d794067a..54ea0d35ab1d 100755 --- a/platform/broadcom/one-image.mk +++ b/platform/broadcom/one-image.mk @@ -27,6 +27,7 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \ $(INVENTEC_D7264Q28B_PLATFORM_MODULE) \ $(CEL_DX010_PLATFORM_MODULE) \ $(CEL_HALIBURTON_PLATFORM_MODULE) \ + $(CEL_SEASTONE2_PLATFORM_MODULE) \ $(DELTA_AG9032V1_PLATFORM_MODULE) \ $(DELTA_AG9064_PLATFORM_MODULE) \ $(DELTA_AG5648_PLATFORM_MODULE) \ @@ -34,8 +35,7 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \ $(QUANTA_IX1B_32X_PLATFORM_MODULE) \ $(MITAC_LY1200_32X_PLATFORM_MODULE) \ $(ALPHANETWORKS_SNH60A0_320FV2_PLATFORM_MODULE) \ - $(ALPHANETWORKS_SNH60B0_640F_PLATFORM_MODULE) \ - $(CEL_SEASTONE2_PLATFORM_MODULE) + $(ALPHANETWORKS_SNH60B0_640F_PLATFORM_MODULE) ifeq ($(INSTALL_DEBUG_TOOLS),y) $(SONIC_ONE_IMAGE)_DOCKERS = $(SONIC_INSTALL_DOCKER_DBG_IMAGES) $(SONIC_ONE_IMAGE)_DOCKERS += $(filter-out $(patsubst %-$(DBG_IMAGE_MARK).gz,%.gz, $(SONIC_INSTALL_DOCKER_DBG_IMAGES)), $(SONIC_INSTALL_DOCKER_IMAGES)) diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/rules b/platform/broadcom/sonic-platform-modules-cel/debian/rules index 44ac6c9d3650..e13880116cf6 100755 --- a/platform/broadcom/sonic-platform-modules-cel/debian/rules +++ b/platform/broadcom/sonic-platform-modules-cel/debian/rules @@ -5,7 +5,7 @@ export INSTALL_MOD_DIR:=extra KVERSION ?= $(shell uname -r) KERNEL_SRC := /lib/modules/$(KVERSION) MOD_SRC_DIR:= $(shell pwd) -MODULE_DIRS:= dx010 haliburton seastone2 +MODULE_DIRS:= dx010 haliburton silverstone seastone2 %: dh $@ @@ -13,9 +13,13 @@ MODULE_DIRS:= dx010 haliburton seastone2 override_dh_auto_build: (for mod in $(MODULE_DIRS); do \ make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ + if [ $$mod = "seastone2" ]; then \ + cd services/platform_api; \ + python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \ + continue; \ + fi; \ cd $(MOD_SRC_DIR)/$${mod}; \ python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \ - cd $(MOD_SRC_DIR); \ done) override_dh_auto_install: @@ -32,5 +36,4 @@ override_dh_clean: dh_clean (for mod in $(MODULE_DIRS); do \ make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules clean; \ - done) - + done) \ No newline at end of file