diff --git a/device/celestica/x86_64-cel_questone_2-r0/Questone_2/custom_led.bin b/device/celestica/x86_64-cel_questone_2-r0/Questone_2/custom_led.bin new file mode 100644 index 000000000000..b2ed8cb8d090 Binary files /dev/null and b/device/celestica/x86_64-cel_questone_2-r0/Questone_2/custom_led.bin differ diff --git a/device/celestica/x86_64-cel_questone_2-r0/Questone_2/platform_components.json b/device/celestica/x86_64-cel_questone_2-r0/Questone_2/platform_components.json new file mode 100644 index 000000000000..8534ef03d89d --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/Questone_2/platform_components.json @@ -0,0 +1,15 @@ +{ + "chassis": { + "Questone_2": { + "component": { + "BIOS": { }, + "BASE_CPLD": { }, + "BMC": { }, + "CPU_CPLD": { }, + "SW_CPLD1": { }, + "SW_CPLD2": { }, + "FPGA": { } + } + } + } +} diff --git a/device/celestica/x86_64-cel_questone_2-r0/Questone_2/port_config.ini b/device/celestica/x86_64-cel_questone_2-r0/Questone_2/port_config.ini new file mode 100644 index 000000000000..9545cd94e81d --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/Questone_2/port_config.ini @@ -0,0 +1,57 @@ +# name lanes alias index speed mtu admin_status fec +Ethernet1 49 25GE1 0 25000 9216 up rs +Ethernet2 50 25GE2 1 25000 9216 up rs +Ethernet3 51 25GE3 2 25000 9216 up rs +Ethernet4 52 25GE4 3 25000 9216 up rs +Ethernet5 57 25GE5 4 25000 9216 up rs +Ethernet6 58 25GE6 5 25000 9216 up rs +Ethernet7 59 25GE7 6 25000 9216 up rs +Ethernet8 60 25GE8 7 25000 9216 up rs +Ethernet9 61 25GE9 8 25000 9216 up rs +Ethernet10 62 25GE10 9 25000 9216 up rs +Ethernet11 63 25GE11 10 25000 9216 up rs +Ethernet12 64 25GE12 11 25000 9216 up rs +Ethernet13 77 25GE13 12 25000 9216 up rs +Ethernet14 78 25GE14 13 25000 9216 up rs +Ethernet15 79 25GE15 14 25000 9216 up rs +Ethernet16 80 25GE16 15 25000 9216 up rs +Ethernet17 85 25GE17 16 25000 9216 up rs +Ethernet18 86 25GE18 17 25000 9216 up rs +Ethernet19 87 25GE19 18 25000 9216 up rs +Ethernet20 88 25GE20 19 25000 9216 up rs +Ethernet21 93 25GE21 20 25000 9216 up rs +Ethernet22 94 25GE22 21 25000 9216 up rs +Ethernet23 95 25GE23 22 25000 9216 up rs +Ethernet24 96 25GE24 23 25000 9216 up rs +Ethernet25 13 25GE25 24 25000 9216 up rs +Ethernet26 14 25GE26 25 25000 9216 up rs +Ethernet27 15 25GE27 26 25000 9216 up rs +Ethernet28 16 25GE28 27 25000 9216 up rs +Ethernet29 21 25GE29 28 25000 9216 up rs +Ethernet30 22 25GE30 29 25000 9216 up rs +Ethernet31 23 25GE31 30 25000 9216 up rs +Ethernet32 24 25GE32 31 25000 9216 up rs +Ethernet33 29 25GE33 32 25000 9216 up rs +Ethernet34 30 25GE34 33 25000 9216 up rs +Ethernet35 31 25GE35 34 25000 9216 up rs +Ethernet36 32 25GE36 35 25000 9216 up rs +Ethernet37 97 25GE37 36 25000 9216 up rs +Ethernet38 98 25GE38 37 25000 9216 up rs +Ethernet39 99 25GE39 38 25000 9216 up rs +Ethernet40 100 25GE40 39 25000 9216 up rs +Ethernet41 105 25GE41 40 25000 9216 up rs +Ethernet42 106 25GE42 41 25000 9216 up rs +Ethernet43 107 25GE43 42 25000 9216 up rs +Ethernet44 108 25GE44 43 25000 9216 up rs +Ethernet45 113 25GE45 44 25000 9216 up rs +Ethernet46 114 25GE46 45 25000 9216 up rs +Ethernet47 115 25GE47 46 25000 9216 up rs +Ethernet48 116 25GE48 47 25000 9216 up rs +Ethernet49 65,66,67,68 100GE1 48 100000 9216 up rs +Ethernet50 69,70,71,72 100GE2 49 100000 9216 up rs +Ethernet51 121,122,123,124 100GE3 50 100000 9216 up rs +Ethernet52 125,126,127,128 100GE4 51 100000 9216 up rs +Ethernet53 1,2,3,4 100GE5 52 100000 9216 up rs +Ethernet54 33,34,35,36 100GE6 53 100000 9216 up rs +Ethernet55 5,6,7,8 100GE7 54 100000 9216 up rs +Ethernet56 41,42,43,44 100GE8 55 100000 9216 up rs diff --git a/device/celestica/x86_64-cel_questone_2-r0/Questone_2/sai.profile b/device/celestica/x86_64-cel_questone_2-r0/Questone_2/sai.profile new file mode 100644 index 000000000000..636d9d72fc17 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/Questone_2/sai.profile @@ -0,0 +1 @@ +SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/td3-as13-48f8h.config.bcm diff --git a/device/celestica/x86_64-cel_questone_2-r0/Questone_2/td3-as13-48f8h.config.bcm b/device/celestica/x86_64-cel_questone_2-r0/Questone_2/td3-as13-48f8h.config.bcm new file mode 100644 index 000000000000..3fe4e4395bcc --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/Questone_2/td3-as13-48f8h.config.bcm @@ -0,0 +1,357 @@ +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=49152 +l3_alpm_enable=2 +bcm_stat_interval=1000000 +host_as_route_disable=1 +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=1 +serdes_lane_config_dfe=on +#serdes_fec_enable=1 +serdes_if_type_xe=13 +serdes_if_type_ce=14 +pbmp_gport_stack.0=0x0000000000000000000000000000000000000000000000000000000000000000 +#pbmp_xport_xe=0x48878787f8787808dfe1e0203e1e1e022 +pbmp_xport_xe=0x8808787f87808088221e1e1fe1e1e1fe + + +portmap_1.0=1:100 +portmap_5.0=5:100 +portmap_13.0=13:25 +portmap_14.0=14:25 +portmap_15.0=15:25 +portmap_16.0=16:25 +portmap_21.0=21:25 +portmap_22.0=22:25 +portmap_23.0=23:25 +portmap_24.0=24:25 +portmap_29.0=29:25 +portmap_30.0=30:25 +portmap_31.0=31:25 +portmap_32.0=32:25 +portmap_33.0=33:100 +portmap_41.0=41:100 +portmap_49.0=49:25 +portmap_50.0=50:25 +portmap_51.0=51:25 +portmap_52.0=52:25 +portmap_57.0=57:25 +portmap_58.0=58:25 +portmap_59.0=59:25 +portmap_60.0=60:25 +portmap_61.0=61:25 +portmap_62.0=62:25 +portmap_63.0=63:25 +portmap_64.0=64:25 +portmap_67.0=65:100 +portmap_71.0=69:100 +portmap_79.0=77:25 +portmap_80.0=78:25 +portmap_81.0=79:25 +portmap_82.0=80:25 +portmap_87.0=85:25 +portmap_88.0=86:25 +portmap_89.0=87:25 +portmap_90.0=88:25 +portmap_95.0=93:25 +portmap_96.0=94:25 +portmap_97.0=95:25 +portmap_98.0=96:25 +portmap_99.0=97:25 +portmap_100.0=98:25 +portmap_101.0=99:25 +portmap_102.0=100:25 +portmap_107.0=105:25 +portmap_108.0=106:25 +portmap_109.0=107:25 +portmap_110.0=108:25 +portmap_115.0=113:25 +portmap_116.0=114:25 +portmap_117.0=115:25 +portmap_118.0=116:25 +portmap_123.0=121:100 +portmap_127.0=125:100 + +phy_chain_tx_lane_map_physical{1.0}=0x0123 +phy_chain_rx_lane_map_physical{1.0}=0x1302 +phy_chain_tx_lane_map_physical{5.0}=0x1032 +phy_chain_rx_lane_map_physical{5.0}=0x1302 +phy_chain_tx_lane_map_physical{13.0}=0x0123 +phy_chain_rx_lane_map_physical{13.0}=0x1032 +phy_chain_tx_lane_map_physical{21.0}=0x0123 +phy_chain_rx_lane_map_physical{21.0}=0x1032 +phy_chain_tx_lane_map_physical{29.0}=0x0123 +phy_chain_rx_lane_map_physical{29.0}=0x1032 +phy_chain_tx_lane_map_physical{33.0}=0x1302 +phy_chain_rx_lane_map_physical{33.0}=0x1032 +phy_chain_tx_lane_map_physical{41.0}=0x3120 +phy_chain_rx_lane_map_physical{41.0}=0x1032 +phy_chain_tx_lane_map_physical{49.0}=0x0123 +phy_chain_rx_lane_map_physical{49.0}=0x1032 +phy_chain_tx_lane_map_physical{57.0}=0x0123 +phy_chain_rx_lane_map_physical{57.0}=0x1032 +phy_chain_tx_lane_map_physical{61.0}=0x0123 +phy_chain_rx_lane_map_physical{61.0}=0x1032 +phy_chain_tx_lane_map_physical{65.0}=0x1302 +phy_chain_rx_lane_map_physical{65.0}=0x1023 +phy_chain_tx_lane_map_physical{69.0}=0x1032 +phy_chain_rx_lane_map_physical{69.0}=0x0213 +phy_chain_tx_lane_map_physical{77.0}=0x3210 +phy_chain_rx_lane_map_physical{77.0}=0x2301 +phy_chain_tx_lane_map_physical{85.0}=0x3210 +phy_chain_rx_lane_map_physical{85.0}=0x2301 +phy_chain_tx_lane_map_physical{93.0}=0x3210 +phy_chain_rx_lane_map_physical{93.0}=0x2301 +phy_chain_tx_lane_map_physical{97.0}=0x3210 +phy_chain_rx_lane_map_physical{97.0}=0x2301 +phy_chain_tx_lane_map_physical{105.0}=0x3210 +phy_chain_rx_lane_map_physical{105.0}=0x2301 +phy_chain_tx_lane_map_physical{113.0}=0x3210 +phy_chain_rx_lane_map_physical{113.0}=0x2301 +phy_chain_tx_lane_map_physical{121.0}=0x2301 +phy_chain_rx_lane_map_physical{121.0}=0x2031 +phy_chain_tx_lane_map_physical{125.0}=0x0132 +phy_chain_rx_lane_map_physical{125.0}=0x3012 +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}=0x1 +phy_chain_rx_polarity_flip_physical{3.0}=0x1 +phy_chain_tx_polarity_flip_physical{4.0}=0x0 +phy_chain_rx_polarity_flip_physical{4.0}=0x1 +phy_chain_tx_polarity_flip_physical{5.0}=0x0 +phy_chain_rx_polarity_flip_physical{5.0}=0x1 +phy_chain_tx_polarity_flip_physical{6.0}=0x1 +phy_chain_rx_polarity_flip_physical{6.0}=0x0 +phy_chain_tx_polarity_flip_physical{7.0}=0x1 +phy_chain_rx_polarity_flip_physical{7.0}=0x0 +phy_chain_tx_polarity_flip_physical{8.0}=0x1 +phy_chain_rx_polarity_flip_physical{8.0}=0x0 +phy_chain_tx_polarity_flip_physical{13.0}=0x0 +phy_chain_rx_polarity_flip_physical{13.0}=0x1 +phy_chain_tx_polarity_flip_physical{14.0}=0x1 +phy_chain_rx_polarity_flip_physical{14.0}=0x0 +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}=0x1 +phy_chain_rx_polarity_flip_physical{16.0}=0x0 +phy_chain_tx_polarity_flip_physical{21.0}=0x0 +phy_chain_rx_polarity_flip_physical{21.0}=0x1 +phy_chain_tx_polarity_flip_physical{22.0}=0x1 +phy_chain_rx_polarity_flip_physical{22.0}=0x0 +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}=0x0 +phy_chain_tx_polarity_flip_physical{29.0}=0x0 +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}=0x1 +phy_chain_tx_polarity_flip_physical{31.0}=0x0 +phy_chain_rx_polarity_flip_physical{31.0}=0x1 +phy_chain_tx_polarity_flip_physical{32.0}=0x1 +phy_chain_rx_polarity_flip_physical{32.0}=0x0 +phy_chain_tx_polarity_flip_physical{33.0}=0x1 +phy_chain_rx_polarity_flip_physical{33.0}=0x0 +phy_chain_tx_polarity_flip_physical{34.0}=0x1 +phy_chain_rx_polarity_flip_physical{34.0}=0x1 +phy_chain_tx_polarity_flip_physical{35.0}=0x1 +phy_chain_rx_polarity_flip_physical{35.0}=0x0 +phy_chain_tx_polarity_flip_physical{36.0}=0x0 +phy_chain_rx_polarity_flip_physical{36.0}=0x0 +phy_chain_tx_polarity_flip_physical{41.0}=0x0 +phy_chain_rx_polarity_flip_physical{41.0}=0x0 +phy_chain_tx_polarity_flip_physical{42.0}=0x1 +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}=0x0 +phy_chain_rx_polarity_flip_physical{44.0}=0x0 +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}=0x0 +phy_chain_rx_polarity_flip_physical{50.0}=0x1 +phy_chain_tx_polarity_flip_physical{51.0}=0x1 +phy_chain_rx_polarity_flip_physical{51.0}=0x0 +phy_chain_tx_polarity_flip_physical{52.0}=0x0 +phy_chain_rx_polarity_flip_physical{52.0}=0x1 +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}=0x0 +phy_chain_rx_polarity_flip_physical{58.0}=0x1 +phy_chain_tx_polarity_flip_physical{59.0}=0x1 +phy_chain_rx_polarity_flip_physical{59.0}=0x0 +phy_chain_tx_polarity_flip_physical{60.0}=0x0 +phy_chain_rx_polarity_flip_physical{60.0}=0x1 +phy_chain_tx_polarity_flip_physical{61.0}=0x1 +phy_chain_rx_polarity_flip_physical{61.0}=0x1 +phy_chain_tx_polarity_flip_physical{62.0}=0x0 +phy_chain_rx_polarity_flip_physical{62.0}=0x0 +phy_chain_tx_polarity_flip_physical{63.0}=0x1 +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 +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}=0x0 +phy_chain_tx_polarity_flip_physical{68.0}=0x1 +phy_chain_rx_polarity_flip_physical{68.0}=0x0 +phy_chain_tx_polarity_flip_physical{69.0}=0x0 +phy_chain_rx_polarity_flip_physical{69.0}=0x1 +phy_chain_tx_polarity_flip_physical{70.0}=0x1 +phy_chain_rx_polarity_flip_physical{70.0}=0x0 +phy_chain_tx_polarity_flip_physical{71.0}=0x0 +phy_chain_rx_polarity_flip_physical{71.0}=0x0 +phy_chain_tx_polarity_flip_physical{72.0}=0x1 +phy_chain_rx_polarity_flip_physical{72.0}=0x1 +phy_chain_tx_polarity_flip_physical{77.0}=0x0 +phy_chain_rx_polarity_flip_physical{77.0}=0x1 +phy_chain_tx_polarity_flip_physical{78.0}=0x1 +phy_chain_rx_polarity_flip_physical{78.0}=0x0 +phy_chain_tx_polarity_flip_physical{79.0}=0x0 +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}=0x0 +phy_chain_tx_polarity_flip_physical{85.0}=0x0 +phy_chain_rx_polarity_flip_physical{85.0}=0x1 +phy_chain_tx_polarity_flip_physical{86.0}=0x1 +phy_chain_rx_polarity_flip_physical{86.0}=0x0 +phy_chain_tx_polarity_flip_physical{87.0}=0x0 +phy_chain_rx_polarity_flip_physical{87.0}=0x1 +phy_chain_tx_polarity_flip_physical{88.0}=0x1 +phy_chain_rx_polarity_flip_physical{88.0}=0x0 +phy_chain_tx_polarity_flip_physical{93.0}=0x0 +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}=0x0 +phy_chain_tx_polarity_flip_physical{95.0}=0x0 +phy_chain_rx_polarity_flip_physical{95.0}=0x1 +phy_chain_tx_polarity_flip_physical{96.0}=0x1 +phy_chain_rx_polarity_flip_physical{96.0}=0x0 +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 +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 +phy_chain_tx_polarity_flip_physical{113.0}=0x1 +phy_chain_rx_polarity_flip_physical{113.0}=0x0 +phy_chain_tx_polarity_flip_physical{114.0}=0x0 +phy_chain_rx_polarity_flip_physical{114.0}=0x1 +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}=0x1 +phy_chain_tx_polarity_flip_physical{121.0}=0x0 +phy_chain_rx_polarity_flip_physical{121.0}=0x1 +phy_chain_tx_polarity_flip_physical{122.0}=0x1 +phy_chain_rx_polarity_flip_physical{122.0}=0x0 +phy_chain_tx_polarity_flip_physical{123.0}=0x0 +phy_chain_rx_polarity_flip_physical{123.0}=0x0 +phy_chain_tx_polarity_flip_physical{124.0}=0x1 +phy_chain_rx_polarity_flip_physical{124.0}=0x0 +phy_chain_tx_polarity_flip_physical{125.0}=0x1 +phy_chain_rx_polarity_flip_physical{125.0}=0x0 +phy_chain_tx_polarity_flip_physical{126.0}=0x1 +phy_chain_rx_polarity_flip_physical{126.0}=0x1 +phy_chain_tx_polarity_flip_physical{127.0}=0x0 +phy_chain_rx_polarity_flip_physical{127.0}=0x0 +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}=0x1 +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}=0x1 +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}=0x1 + +dport_map_port_49=1 +dport_map_port_50=2 +dport_map_port_51=3 +dport_map_port_52=4 +dport_map_port_57=5 +dport_map_port_58=6 +dport_map_port_59=7 +dport_map_port_60=8 +dport_map_port_61=9 +dport_map_port_62=10 +dport_map_port_63=11 +dport_map_port_64=12 +dport_map_port_79=13 +dport_map_port_80=14 +dport_map_port_81=15 +dport_map_port_82=16 +dport_map_port_87=17 +dport_map_port_88=18 +dport_map_port_89=19 +dport_map_port_90=20 +dport_map_port_95=21 +dport_map_port_96=22 +dport_map_port_97=23 +dport_map_port_98=24 +dport_map_port_13=25 +dport_map_port_14=26 +dport_map_port_15=27 +dport_map_port_16=28 +dport_map_port_21=29 +dport_map_port_22=30 +dport_map_port_23=31 +dport_map_port_24=32 +dport_map_port_29=33 +dport_map_port_30=34 +dport_map_port_31=35 +dport_map_port_32=36 +dport_map_port_99=37 +dport_map_port_100=38 +dport_map_port_101=39 +dport_map_port_102=40 +dport_map_port_107=41 +dport_map_port_108=42 +dport_map_port_109=43 +dport_map_port_110=44 +dport_map_port_115=45 +dport_map_port_116=46 +dport_map_port_117=47 +dport_map_port_118=48 +dport_map_port_67=49 +dport_map_port_71=50 +dport_map_port_123=51 +dport_map_port_127=52 +dport_map_port_1=53 +dport_map_port_33=54 +dport_map_port_5=55 +dport_map_port_41=56 + +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_questone_2-r0/Questone_2A/custom_led.bin b/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/custom_led.bin new file mode 100755 index 000000000000..fb33257fbbe9 Binary files /dev/null and b/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/custom_led.bin differ diff --git a/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/platform_components.json b/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/platform_components.json new file mode 100644 index 000000000000..b12b3ac5b187 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/platform_components.json @@ -0,0 +1,15 @@ +{ + "chassis": { + "Questone_2A": { + "component": { + "BIOS": { }, + "BASE_CPLD": { }, + "BMC": { }, + "CPU_CPLD": { }, + "SW_CPLD1": { }, + "SW_CPLD2": { }, + "FPGA": { } + } + } + } +} diff --git a/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/port_config.ini b/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/port_config.ini new file mode 100644 index 000000000000..3bfebfd34835 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/port_config.ini @@ -0,0 +1,57 @@ +# name lanes alias index speed mtu admin_status fec +Ethernet1 29 25GE1 0 25000 9216 up rs +Ethernet2 30 25GE2 1 25000 9216 up rs +Ethernet3 31 25GE3 2 25000 9216 up rs +Ethernet4 32 25GE4 3 25000 9216 up rs +Ethernet5 33 25GE5 4 25000 9216 up rs +Ethernet6 34 25GE6 5 25000 9216 up rs +Ethernet7 35 25GE7 6 25000 9216 up rs +Ethernet8 36 25GE8 7 25000 9216 up rs +Ethernet9 37 25GE9 8 25000 9216 up rs +Ethernet10 38 25GE10 9 25000 9216 up rs +Ethernet11 39 25GE11 10 25000 9216 up rs +Ethernet12 40 25GE12 11 25000 9216 up rs +Ethernet13 49 25GE13 12 25000 9216 up rs +Ethernet14 50 25GE14 13 25000 9216 up rs +Ethernet15 51 25GE15 14 25000 9216 up rs +Ethernet16 52 25GE16 15 25000 9216 up rs +Ethernet17 53 25GE17 16 25000 9216 up rs +Ethernet18 54 25GE18 17 25000 9216 up rs +Ethernet19 55 25GE19 18 25000 9216 up rs +Ethernet20 56 25GE20 19 25000 9216 up rs +Ethernet21 57 25GE21 20 25000 9216 up rs +Ethernet22 58 25GE22 21 25000 9216 up rs +Ethernet23 59 25GE23 22 25000 9216 up rs +Ethernet24 60 25GE24 23 25000 9216 up rs +Ethernet25 9 25GE25 24 25000 9216 up rs +Ethernet26 10 25GE26 25 25000 9216 up rs +Ethernet27 11 25GE27 26 25000 9216 up rs +Ethernet28 12 25GE28 27 25000 9216 up rs +Ethernet29 13 25GE29 28 25000 9216 up rs +Ethernet30 14 25GE30 29 25000 9216 up rs +Ethernet31 15 25GE31 30 25000 9216 up rs +Ethernet32 16 25GE32 31 25000 9216 up rs +Ethernet33 17 25GE33 32 25000 9216 up rs +Ethernet34 18 25GE34 33 25000 9216 up rs +Ethernet35 19 25GE35 34 25000 9216 up rs +Ethernet36 20 25GE36 35 25000 9216 up rs +Ethernet37 61 25GE37 36 25000 9216 up rs +Ethernet38 62 25GE38 37 25000 9216 up rs +Ethernet39 63 25GE39 38 25000 9216 up rs +Ethernet40 64 25GE40 39 25000 9216 up rs +Ethernet41 65 25GE41 40 25000 9216 up rs +Ethernet42 66 25GE42 41 25000 9216 up rs +Ethernet43 67 25GE43 42 25000 9216 up rs +Ethernet44 68 25GE44 43 25000 9216 up rs +Ethernet45 69 25GE45 44 25000 9216 up rs +Ethernet46 70 25GE46 45 25000 9216 up rs +Ethernet47 71 25GE47 46 25000 9216 up rs +Ethernet48 72 25GE48 47 25000 9216 up rs +Ethernet49 41,42,43,44 100GE1 48 100000 9216 up rs +Ethernet50 45,46,47,48 100GE2 49 100000 9216 up rs +Ethernet51 73,74,75,76 100GE3 50 100000 9216 up rs +Ethernet52 77,78,79,80 100GE4 51 100000 9216 up rs +Ethernet53 1,2,3,4 100GE5 52 100000 9216 up rs +Ethernet54 21,22,23,24 100GE6 53 100000 9216 up rs +Ethernet55 5,6,7,8, 100GE7 54 100000 9216 up rs +Ethernet56 25,26,27,28 100GE8 55 100000 9216 up rs diff --git a/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/sai.profile.j2 b/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/sai.profile.j2 new file mode 100644 index 000000000000..62a1db9e0960 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/sai.profile.j2 @@ -0,0 +1,16 @@ +{# Get sai.profile based on vxlan_profile. Vxlan's config.bcm file is the default one #} +{%- set sai_profile_contents = 'SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/td3-as13-48f8h-vxlan.config.bcm' -%} +{%- if DEVICE_METADATA is defined and DEVICE_METADATA['localhost'] is defined -%} +{%- if DEVICE_METADATA['localhost']['vxlan_profile'] is defined -%} +{%- set vxlan_profile = DEVICE_METADATA['localhost']['vxlan_profile'] -%} +{%- if 'enable' in vxlan_profile.lower() %} +{% set sai_profile_contents = 'SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/td3-as13-48f8h-vxlan.config.bcm' -%} +{%- else %} +{%- set sai_profile_contents = 'SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/td3-as13-48f8h.config.bcm' -%} +{%- endif %} +{%- else %} +{%- set sai_profile_contents = 'SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/td3-as13-48f8h-vxlan.config.bcm' -%} +{%- endif %} +{%- endif %} +{# Write the contents of sai_profile_filename to sai.profile file #} +{{ sai_profile_contents }} diff --git a/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/td3-as13-48f8h-vxlan.config.bcm b/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/td3-as13-48f8h-vxlan.config.bcm new file mode 100755 index 000000000000..7d444dfb53f6 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/td3-as13-48f8h-vxlan.config.bcm @@ -0,0 +1,423 @@ +ptp_ts_pll_fref=50000000 +ptp_bs_fref_0=50000000 +ptp_bs_fref_1=50000000 +oversubscribe_mode=1 +pbmp_xport_xe=0x11ffffffe1ffffffe +stable_size=0x5500000 +ifp_inports_support_enable=1 + +#FC0 +portmap_1.0=1:100 + +#FC1 +portmap_2.0=5:100 + +#FC2 +portmap_3.0=9:25 +portmap_4.0=10:25 +portmap_5.0=11:25 +portmap_6.0=12:25 + +#FC3 +portmap_7.0=13:25 +portmap_8.0=14:25 +portmap_9.0=15:25 +portmap_10.0=16:25 + +#FC4 +portmap_11.0=17:25 +portmap_12.0=18:25 +portmap_13.0=19:25 +portmap_14.0=20:25 + +#FC5 +portmap_15.0=21:100 + +#FC6 +portmap_16.0=25:100 + +#FC7 +portmap_17.0=29:25 +portmap_18.0=30:25 +portmap_19.0=31:25 +portmap_20.0=32:25 + +#FC8 +portmap_21.0=33:25 +portmap_22.0=34:25 +portmap_23.0=35:25 +portmap_24.0=36:25 + +#FC9 +portmap_25.0=37:25 +portmap_26.0=38:25 +portmap_27.0=39:25 +portmap_28.0=40:25 + +#FC10 +portmap_33.0=41:100 + +#FC11 +portmap_34.0=45:100 + +#FC12 +portmap_35.0=49:25 +portmap_36.0=50:25 +portmap_37.0=51:25 +portmap_38.0=52:25 + +#FC13 +portmap_39.0=53:25 +portmap_40.0=54:25 +portmap_41.0=55:25 +portmap_42.0=56:25 + +#FC14 +portmap_43.0=57:25 +portmap_44.0=58:25 +portmap_45.0=59:25 +portmap_46.0=60:25 + +#FC15 +portmap_47.0=61:25 +portmap_48.0=62:25 +portmap_49.0=63:25 +portmap_50.0=64:25 + +#FC16 +portmap_51.0=65:25 +portmap_52.0=66:25 +portmap_53.0=67:25 +portmap_54.0=68:25 + +#FC17 +portmap_55.0=69:25 +portmap_56.0=70:25 +portmap_57.0=71:25 +portmap_58.0=72:25 + +#FC18 +portmap_59.0=73:100 + +#FC19 +portmap_60.0=77:100 + + +#portmap_64.0=81:10 + +phy_chain_tx_lane_map_physical{1.0}=0x0123 +phy_chain_rx_lane_map_physical{1.0}=0x1302 +phy_chain_tx_lane_map_physical{5.0}=0x1032 +phy_chain_rx_lane_map_physical{5.0}=0x1302 +phy_chain_tx_lane_map_physical{9.0}=0x0123 +phy_chain_rx_lane_map_physical{9.0}=0x1032 +phy_chain_tx_lane_map_physical{13.0}=0x0123 +phy_chain_rx_lane_map_physical{13.0}=0x1032 +phy_chain_tx_lane_map_physical{17.0}=0x0123 +phy_chain_rx_lane_map_physical{17.0}=0x1032 +phy_chain_tx_lane_map_physical{21.0}=0x1302 +phy_chain_rx_lane_map_physical{21.0}=0x1032 +phy_chain_tx_lane_map_physical{25.0}=0x3120 +phy_chain_rx_lane_map_physical{25.0}=0x1032 +phy_chain_tx_lane_map_physical{29.0}=0x0123 +phy_chain_rx_lane_map_physical{29.0}=0x1032 +phy_chain_tx_lane_map_physical{33.0}=0x0123 +phy_chain_rx_lane_map_physical{33.0}=0x1032 +phy_chain_tx_lane_map_physical{37.0}=0x0123 +phy_chain_rx_lane_map_physical{37.0}=0x1032 +phy_chain_tx_lane_map_physical{41.0}=0x1302 +phy_chain_rx_lane_map_physical{41.0}=0x1023 +phy_chain_tx_lane_map_physical{45.0}=0x1032 +phy_chain_rx_lane_map_physical{45.0}=0x0213 +phy_chain_tx_lane_map_physical{49.0}=0x3210 +phy_chain_rx_lane_map_physical{49.0}=0x2301 +phy_chain_tx_lane_map_physical{53.0}=0x3210 +phy_chain_rx_lane_map_physical{53.0}=0x2301 +phy_chain_tx_lane_map_physical{57.0}=0x3210 +phy_chain_rx_lane_map_physical{57.0}=0x2301 +phy_chain_tx_lane_map_physical{61.0}=0x3210 +phy_chain_rx_lane_map_physical{61.0}=0x2301 +phy_chain_tx_lane_map_physical{65.0}=0x3210 +phy_chain_rx_lane_map_physical{65.0}=0x2301 +phy_chain_tx_lane_map_physical{69.0}=0x3210 +phy_chain_rx_lane_map_physical{69.0}=0x2301 +phy_chain_tx_lane_map_physical{73.0}=0x2301 +phy_chain_rx_lane_map_physical{73.0}=0x2031 +phy_chain_tx_lane_map_physical{77.0}=0x0132 +phy_chain_rx_lane_map_physical{77.0}=0x3012 +phy_chain_tx_lane_map_physical{81.0}=0x3210 +phy_chain_rx_lane_map_physical{81.0}=0x3201 + +#FC0 +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}=0x1 +phy_chain_rx_polarity_flip_physical{3.0}=0x1 +phy_chain_tx_polarity_flip_physical{4.0}=0x0 +phy_chain_rx_polarity_flip_physical{4.0}=0x1 + +#FC1 +phy_chain_tx_polarity_flip_physical{5.0}=0x0 +phy_chain_rx_polarity_flip_physical{5.0}=0x1 +phy_chain_tx_polarity_flip_physical{6.0}=0x1 +phy_chain_rx_polarity_flip_physical{6.0}=0x0 +phy_chain_tx_polarity_flip_physical{7.0}=0x1 +phy_chain_rx_polarity_flip_physical{7.0}=0x0 +phy_chain_tx_polarity_flip_physical{8.0}=0x1 +phy_chain_rx_polarity_flip_physical{8.0}=0x0 + +#FC2 +phy_chain_tx_polarity_flip_physical{9.0}=0x0 +phy_chain_rx_polarity_flip_physical{9.0}=0x1 +phy_chain_tx_polarity_flip_physical{10.0}=0x1 +phy_chain_rx_polarity_flip_physical{10.0}=0x0 +phy_chain_tx_polarity_flip_physical{11.0}=0x0 +phy_chain_rx_polarity_flip_physical{11.0}=0x1 +phy_chain_tx_polarity_flip_physical{12.0}=0x1 +phy_chain_rx_polarity_flip_physical{12.0}=0x0 + +#FC3 +phy_chain_tx_polarity_flip_physical{13.0}=0x0 +phy_chain_rx_polarity_flip_physical{13.0}=0x1 +phy_chain_tx_polarity_flip_physical{14.0}=0x1 +phy_chain_rx_polarity_flip_physical{14.0}=0x0 +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}=0x1 +phy_chain_rx_polarity_flip_physical{16.0}=0x0 + +#FC4 +phy_chain_tx_polarity_flip_physical{17.0}=0x0 +phy_chain_rx_polarity_flip_physical{17.0}=0x1 +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}=0x1 +phy_chain_tx_polarity_flip_physical{20.0}=0x1 +phy_chain_rx_polarity_flip_physical{20.0}=0x0 + +#FC5 +phy_chain_tx_polarity_flip_physical{21.0}=0x1 +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}=0x1 +phy_chain_rx_polarity_flip_physical{23.0}=0x0 +phy_chain_tx_polarity_flip_physical{24.0}=0x0 +phy_chain_rx_polarity_flip_physical{24.0}=0x0 + +#FC6 +phy_chain_tx_polarity_flip_physical{25.0}=0x0 +phy_chain_rx_polarity_flip_physical{25.0}=0x0 +phy_chain_tx_polarity_flip_physical{26.0}=0x1 +phy_chain_rx_polarity_flip_physical{26.0}=0x1 +phy_chain_tx_polarity_flip_physical{27.0}=0x1 +phy_chain_rx_polarity_flip_physical{27.0}=0x0 +phy_chain_tx_polarity_flip_physical{28.0}=0x0 +phy_chain_rx_polarity_flip_physical{28.0}=0x0 + +#FC7 +phy_chain_tx_polarity_flip_physical{29.0}=0x1 +phy_chain_rx_polarity_flip_physical{29.0}=0x0 +phy_chain_tx_polarity_flip_physical{30.0}=0x0 +phy_chain_rx_polarity_flip_physical{30.0}=0x1 +phy_chain_tx_polarity_flip_physical{31.0}=0x1 +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}=0x1 + +#FC8 +phy_chain_tx_polarity_flip_physical{33.0}=0x1 +phy_chain_rx_polarity_flip_physical{33.0}=0x0 +phy_chain_tx_polarity_flip_physical{34.0}=0x0 +phy_chain_rx_polarity_flip_physical{34.0}=0x1 +phy_chain_tx_polarity_flip_physical{35.0}=0x1 +phy_chain_rx_polarity_flip_physical{35.0}=0x0 +phy_chain_tx_polarity_flip_physical{36.0}=0x0 +phy_chain_rx_polarity_flip_physical{36.0}=0x1 + +#FC9 +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}=0x0 +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}=0x1 +phy_chain_tx_polarity_flip_physical{40.0}=0x0 +phy_chain_rx_polarity_flip_physical{40.0}=0x0 + +#FC10 +phy_chain_tx_polarity_flip_physical{41.0}=0x1 +phy_chain_rx_polarity_flip_physical{41.0}=0x0 +phy_chain_tx_polarity_flip_physical{42.0}=0x0 +phy_chain_rx_polarity_flip_physical{42.0}=0x0 +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}=0x0 + +#FC11 +phy_chain_tx_polarity_flip_physical{45.0}=0x0 +phy_chain_rx_polarity_flip_physical{45.0}=0x1 +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}=0x0 +phy_chain_rx_polarity_flip_physical{47.0}=0x0 +phy_chain_tx_polarity_flip_physical{48.0}=0x1 +phy_chain_rx_polarity_flip_physical{48.0}=0x1 + +#FC12 +phy_chain_tx_polarity_flip_physical{49.0}=0x0 +phy_chain_rx_polarity_flip_physical{49.0}=0x1 +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}=0x0 + +#FC13 +phy_chain_tx_polarity_flip_physical{53.0}=0x0 +phy_chain_rx_polarity_flip_physical{53.0}=0x1 +phy_chain_tx_polarity_flip_physical{54.0}=0x1 +phy_chain_rx_polarity_flip_physical{54.0}=0x0 +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}=0x0 + +#FC14 +phy_chain_tx_polarity_flip_physical{57.0}=0x0 +phy_chain_rx_polarity_flip_physical{57.0}=0x1 +phy_chain_tx_polarity_flip_physical{58.0}=0x1 +phy_chain_rx_polarity_flip_physical{58.0}=0x0 +phy_chain_tx_polarity_flip_physical{59.0}=0x0 +phy_chain_rx_polarity_flip_physical{59.0}=0x1 +phy_chain_tx_polarity_flip_physical{60.0}=0x1 +phy_chain_rx_polarity_flip_physical{60.0}=0x0 + +#FC15 +phy_chain_tx_polarity_flip_physical{61.0}=0x1 +phy_chain_rx_polarity_flip_physical{61.0}=0x1 +phy_chain_tx_polarity_flip_physical{62.0}=0x0 +phy_chain_rx_polarity_flip_physical{62.0}=0x0 +phy_chain_tx_polarity_flip_physical{63.0}=0x1 +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 + +#FC16 +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}=0x1 +phy_chain_tx_polarity_flip_physical{67.0}=0x1 +phy_chain_rx_polarity_flip_physical{67.0}=0x0 +phy_chain_tx_polarity_flip_physical{68.0}=0x0 +phy_chain_rx_polarity_flip_physical{68.0}=0x1 + +#FC17 +phy_chain_tx_polarity_flip_physical{69.0}=0x1 +phy_chain_rx_polarity_flip_physical{69.0}=0x0 +phy_chain_tx_polarity_flip_physical{70.0}=0x0 +phy_chain_rx_polarity_flip_physical{70.0}=0x1 +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}=0x1 + +#FC18 +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}=0x0 +phy_chain_tx_polarity_flip_physical{76.0}=0x1 +phy_chain_rx_polarity_flip_physical{76.0}=0x0 + +#FC19 +phy_chain_tx_polarity_flip_physical{77.0}=0x1 +phy_chain_rx_polarity_flip_physical{77.0}=0x0 +phy_chain_tx_polarity_flip_physical{78.0}=0x1 +phy_chain_rx_polarity_flip_physical{78.0}=0x1 +phy_chain_tx_polarity_flip_physical{79.0}=0x0 +phy_chain_rx_polarity_flip_physical{79.0}=0x0 +phy_chain_tx_polarity_flip_physical{80.0}=0x0 +phy_chain_rx_polarity_flip_physical{80.0}=0x0 + + +phy_chain_tx_polarity_flip_physical{81.0}=0x0 +phy_chain_rx_polarity_flip_physical{81.0}=0x1 + + +dport_map_port_17=1 +dport_map_port_18=2 +dport_map_port_19=3 +dport_map_port_20=4 +dport_map_port_21=5 +dport_map_port_22=6 +dport_map_port_23=7 +dport_map_port_24=8 +dport_map_port_25=9 +dport_map_port_26=10 +dport_map_port_27=11 +dport_map_port_28=12 +dport_map_port_35=13 +dport_map_port_36=14 +dport_map_port_37=15 +dport_map_port_38=16 +dport_map_port_39=17 +dport_map_port_40=18 +dport_map_port_41=19 +dport_map_port_42=20 +dport_map_port_43=21 +dport_map_port_44=22 +dport_map_port_45=23 +dport_map_port_46=24 +dport_map_port_3=25 +dport_map_port_4=26 +dport_map_port_5=27 +dport_map_port_6=28 +dport_map_port_7=29 +dport_map_port_8=30 +dport_map_port_9=31 +dport_map_port_10=32 +dport_map_port_11=33 +dport_map_port_12=34 +dport_map_port_13=35 +dport_map_port_14=36 +dport_map_port_47=37 +dport_map_port_48=38 +dport_map_port_49=39 +dport_map_port_50=40 +dport_map_port_51=41 +dport_map_port_52=42 +dport_map_port_53=43 +dport_map_port_54=44 +dport_map_port_55=45 +dport_map_port_56=46 +dport_map_port_57=47 +dport_map_port_58=48 +dport_map_port_33=49 +dport_map_port_34=50 +dport_map_port_59=51 +dport_map_port_60=52 +dport_map_port_1=53 +dport_map_port_15=54 +dport_map_port_2=55 +dport_map_port_16=56 +#dport_map_port_64=57 + +#VxLAN +sai_tunnel_support=1 +use_all_splithorizon_groups=1 +bcm_tunnel_term_compatible_mode=1 +flow_init_mode=1 +l3_ecmp_levels=2 +riot_enable=1 +riot_overlay_l3_intf_mem_size=8192 +riot_overlay_l3_egress_mem_size=32768 +riot_overlay_ecmp_resilient_hash_size=16384 diff --git a/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/td3-as13-48f8h.config.bcm b/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/td3-as13-48f8h.config.bcm new file mode 100755 index 000000000000..303b92dd39a5 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/Questone_2A/td3-as13-48f8h.config.bcm @@ -0,0 +1,412 @@ +ptp_ts_pll_fref=50000000 +ptp_bs_fref_0=50000000 +ptp_bs_fref_1=50000000 +oversubscribe_mode=1 +pbmp_xport_xe=0x11ffffffe1ffffffe +stable_size=0x5500000 +ifp_inports_support_enable=1 + +#FC0 +portmap_1.0=1:100 + +#FC1 +portmap_2.0=5:100 + +#FC2 +portmap_3.0=9:25 +portmap_4.0=10:25 +portmap_5.0=11:25 +portmap_6.0=12:25 + +#FC3 +portmap_7.0=13:25 +portmap_8.0=14:25 +portmap_9.0=15:25 +portmap_10.0=16:25 + +#FC4 +portmap_11.0=17:25 +portmap_12.0=18:25 +portmap_13.0=19:25 +portmap_14.0=20:25 + +#FC5 +portmap_15.0=21:100 + +#FC6 +portmap_16.0=25:100 + +#FC7 +portmap_17.0=29:25 +portmap_18.0=30:25 +portmap_19.0=31:25 +portmap_20.0=32:25 + +#FC8 +portmap_21.0=33:25 +portmap_22.0=34:25 +portmap_23.0=35:25 +portmap_24.0=36:25 + +#FC9 +portmap_25.0=37:25 +portmap_26.0=38:25 +portmap_27.0=39:25 +portmap_28.0=40:25 + +#FC10 +portmap_33.0=41:100 + +#FC11 +portmap_34.0=45:100 + +#FC12 +portmap_35.0=49:25 +portmap_36.0=50:25 +portmap_37.0=51:25 +portmap_38.0=52:25 + +#FC13 +portmap_39.0=53:25 +portmap_40.0=54:25 +portmap_41.0=55:25 +portmap_42.0=56:25 + +#FC14 +portmap_43.0=57:25 +portmap_44.0=58:25 +portmap_45.0=59:25 +portmap_46.0=60:25 + +#FC15 +portmap_47.0=61:25 +portmap_48.0=62:25 +portmap_49.0=63:25 +portmap_50.0=64:25 + +#FC16 +portmap_51.0=65:25 +portmap_52.0=66:25 +portmap_53.0=67:25 +portmap_54.0=68:25 + +#FC17 +portmap_55.0=69:25 +portmap_56.0=70:25 +portmap_57.0=71:25 +portmap_58.0=72:25 + +#FC18 +portmap_59.0=73:100 + +#FC19 +portmap_60.0=77:100 + + +#portmap_64.0=81:10 + +phy_chain_tx_lane_map_physical{1.0}=0x0123 +phy_chain_rx_lane_map_physical{1.0}=0x1302 +phy_chain_tx_lane_map_physical{5.0}=0x1032 +phy_chain_rx_lane_map_physical{5.0}=0x1302 +phy_chain_tx_lane_map_physical{9.0}=0x0123 +phy_chain_rx_lane_map_physical{9.0}=0x1032 +phy_chain_tx_lane_map_physical{13.0}=0x0123 +phy_chain_rx_lane_map_physical{13.0}=0x1032 +phy_chain_tx_lane_map_physical{17.0}=0x0123 +phy_chain_rx_lane_map_physical{17.0}=0x1032 +phy_chain_tx_lane_map_physical{21.0}=0x1302 +phy_chain_rx_lane_map_physical{21.0}=0x1032 +phy_chain_tx_lane_map_physical{25.0}=0x3120 +phy_chain_rx_lane_map_physical{25.0}=0x1032 +phy_chain_tx_lane_map_physical{29.0}=0x0123 +phy_chain_rx_lane_map_physical{29.0}=0x1032 +phy_chain_tx_lane_map_physical{33.0}=0x0123 +phy_chain_rx_lane_map_physical{33.0}=0x1032 +phy_chain_tx_lane_map_physical{37.0}=0x0123 +phy_chain_rx_lane_map_physical{37.0}=0x1032 +phy_chain_tx_lane_map_physical{41.0}=0x1302 +phy_chain_rx_lane_map_physical{41.0}=0x1023 +phy_chain_tx_lane_map_physical{45.0}=0x1032 +phy_chain_rx_lane_map_physical{45.0}=0x0213 +phy_chain_tx_lane_map_physical{49.0}=0x3210 +phy_chain_rx_lane_map_physical{49.0}=0x2301 +phy_chain_tx_lane_map_physical{53.0}=0x3210 +phy_chain_rx_lane_map_physical{53.0}=0x2301 +phy_chain_tx_lane_map_physical{57.0}=0x3210 +phy_chain_rx_lane_map_physical{57.0}=0x2301 +phy_chain_tx_lane_map_physical{61.0}=0x3210 +phy_chain_rx_lane_map_physical{61.0}=0x2301 +phy_chain_tx_lane_map_physical{65.0}=0x3210 +phy_chain_rx_lane_map_physical{65.0}=0x2301 +phy_chain_tx_lane_map_physical{69.0}=0x3210 +phy_chain_rx_lane_map_physical{69.0}=0x2301 +phy_chain_tx_lane_map_physical{73.0}=0x2301 +phy_chain_rx_lane_map_physical{73.0}=0x2031 +phy_chain_tx_lane_map_physical{77.0}=0x0132 +phy_chain_rx_lane_map_physical{77.0}=0x3012 +phy_chain_tx_lane_map_physical{81.0}=0x3210 +phy_chain_rx_lane_map_physical{81.0}=0x3201 + +#FC0 +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}=0x1 +phy_chain_rx_polarity_flip_physical{3.0}=0x1 +phy_chain_tx_polarity_flip_physical{4.0}=0x0 +phy_chain_rx_polarity_flip_physical{4.0}=0x1 + +#FC1 +phy_chain_tx_polarity_flip_physical{5.0}=0x0 +phy_chain_rx_polarity_flip_physical{5.0}=0x1 +phy_chain_tx_polarity_flip_physical{6.0}=0x1 +phy_chain_rx_polarity_flip_physical{6.0}=0x0 +phy_chain_tx_polarity_flip_physical{7.0}=0x1 +phy_chain_rx_polarity_flip_physical{7.0}=0x0 +phy_chain_tx_polarity_flip_physical{8.0}=0x1 +phy_chain_rx_polarity_flip_physical{8.0}=0x0 + +#FC2 +phy_chain_tx_polarity_flip_physical{9.0}=0x0 +phy_chain_rx_polarity_flip_physical{9.0}=0x1 +phy_chain_tx_polarity_flip_physical{10.0}=0x1 +phy_chain_rx_polarity_flip_physical{10.0}=0x0 +phy_chain_tx_polarity_flip_physical{11.0}=0x0 +phy_chain_rx_polarity_flip_physical{11.0}=0x1 +phy_chain_tx_polarity_flip_physical{12.0}=0x1 +phy_chain_rx_polarity_flip_physical{12.0}=0x0 + +#FC3 +phy_chain_tx_polarity_flip_physical{13.0}=0x0 +phy_chain_rx_polarity_flip_physical{13.0}=0x1 +phy_chain_tx_polarity_flip_physical{14.0}=0x1 +phy_chain_rx_polarity_flip_physical{14.0}=0x0 +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}=0x1 +phy_chain_rx_polarity_flip_physical{16.0}=0x0 + +#FC4 +phy_chain_tx_polarity_flip_physical{17.0}=0x0 +phy_chain_rx_polarity_flip_physical{17.0}=0x1 +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}=0x1 +phy_chain_tx_polarity_flip_physical{20.0}=0x1 +phy_chain_rx_polarity_flip_physical{20.0}=0x0 + +#FC5 +phy_chain_tx_polarity_flip_physical{21.0}=0x1 +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}=0x1 +phy_chain_rx_polarity_flip_physical{23.0}=0x0 +phy_chain_tx_polarity_flip_physical{24.0}=0x0 +phy_chain_rx_polarity_flip_physical{24.0}=0x0 + +#FC6 +phy_chain_tx_polarity_flip_physical{25.0}=0x0 +phy_chain_rx_polarity_flip_physical{25.0}=0x0 +phy_chain_tx_polarity_flip_physical{26.0}=0x1 +phy_chain_rx_polarity_flip_physical{26.0}=0x1 +phy_chain_tx_polarity_flip_physical{27.0}=0x1 +phy_chain_rx_polarity_flip_physical{27.0}=0x0 +phy_chain_tx_polarity_flip_physical{28.0}=0x0 +phy_chain_rx_polarity_flip_physical{28.0}=0x0 + +#FC7 +phy_chain_tx_polarity_flip_physical{29.0}=0x1 +phy_chain_rx_polarity_flip_physical{29.0}=0x0 +phy_chain_tx_polarity_flip_physical{30.0}=0x0 +phy_chain_rx_polarity_flip_physical{30.0}=0x1 +phy_chain_tx_polarity_flip_physical{31.0}=0x1 +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}=0x1 + +#FC8 +phy_chain_tx_polarity_flip_physical{33.0}=0x1 +phy_chain_rx_polarity_flip_physical{33.0}=0x0 +phy_chain_tx_polarity_flip_physical{34.0}=0x0 +phy_chain_rx_polarity_flip_physical{34.0}=0x1 +phy_chain_tx_polarity_flip_physical{35.0}=0x1 +phy_chain_rx_polarity_flip_physical{35.0}=0x0 +phy_chain_tx_polarity_flip_physical{36.0}=0x0 +phy_chain_rx_polarity_flip_physical{36.0}=0x1 + +#FC9 +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}=0x0 +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}=0x1 +phy_chain_tx_polarity_flip_physical{40.0}=0x0 +phy_chain_rx_polarity_flip_physical{40.0}=0x0 + +#FC10 +phy_chain_tx_polarity_flip_physical{41.0}=0x1 +phy_chain_rx_polarity_flip_physical{41.0}=0x0 +phy_chain_tx_polarity_flip_physical{42.0}=0x0 +phy_chain_rx_polarity_flip_physical{42.0}=0x0 +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}=0x0 + +#FC11 +phy_chain_tx_polarity_flip_physical{45.0}=0x0 +phy_chain_rx_polarity_flip_physical{45.0}=0x1 +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}=0x0 +phy_chain_rx_polarity_flip_physical{47.0}=0x0 +phy_chain_tx_polarity_flip_physical{48.0}=0x1 +phy_chain_rx_polarity_flip_physical{48.0}=0x1 + +#FC12 +phy_chain_tx_polarity_flip_physical{49.0}=0x0 +phy_chain_rx_polarity_flip_physical{49.0}=0x1 +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}=0x0 + +#FC13 +phy_chain_tx_polarity_flip_physical{53.0}=0x0 +phy_chain_rx_polarity_flip_physical{53.0}=0x1 +phy_chain_tx_polarity_flip_physical{54.0}=0x1 +phy_chain_rx_polarity_flip_physical{54.0}=0x0 +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}=0x0 + +#FC14 +phy_chain_tx_polarity_flip_physical{57.0}=0x0 +phy_chain_rx_polarity_flip_physical{57.0}=0x1 +phy_chain_tx_polarity_flip_physical{58.0}=0x1 +phy_chain_rx_polarity_flip_physical{58.0}=0x0 +phy_chain_tx_polarity_flip_physical{59.0}=0x0 +phy_chain_rx_polarity_flip_physical{59.0}=0x1 +phy_chain_tx_polarity_flip_physical{60.0}=0x1 +phy_chain_rx_polarity_flip_physical{60.0}=0x0 + +#FC15 +phy_chain_tx_polarity_flip_physical{61.0}=0x1 +phy_chain_rx_polarity_flip_physical{61.0}=0x1 +phy_chain_tx_polarity_flip_physical{62.0}=0x0 +phy_chain_rx_polarity_flip_physical{62.0}=0x0 +phy_chain_tx_polarity_flip_physical{63.0}=0x1 +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 + +#FC16 +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}=0x1 +phy_chain_tx_polarity_flip_physical{67.0}=0x1 +phy_chain_rx_polarity_flip_physical{67.0}=0x0 +phy_chain_tx_polarity_flip_physical{68.0}=0x0 +phy_chain_rx_polarity_flip_physical{68.0}=0x1 + +#FC17 +phy_chain_tx_polarity_flip_physical{69.0}=0x1 +phy_chain_rx_polarity_flip_physical{69.0}=0x0 +phy_chain_tx_polarity_flip_physical{70.0}=0x0 +phy_chain_rx_polarity_flip_physical{70.0}=0x1 +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}=0x1 + +#FC18 +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}=0x0 +phy_chain_tx_polarity_flip_physical{76.0}=0x1 +phy_chain_rx_polarity_flip_physical{76.0}=0x0 + +#FC19 +phy_chain_tx_polarity_flip_physical{77.0}=0x1 +phy_chain_rx_polarity_flip_physical{77.0}=0x0 +phy_chain_tx_polarity_flip_physical{78.0}=0x1 +phy_chain_rx_polarity_flip_physical{78.0}=0x1 +phy_chain_tx_polarity_flip_physical{79.0}=0x0 +phy_chain_rx_polarity_flip_physical{79.0}=0x0 +phy_chain_tx_polarity_flip_physical{80.0}=0x0 +phy_chain_rx_polarity_flip_physical{80.0}=0x0 + + +phy_chain_tx_polarity_flip_physical{81.0}=0x0 +phy_chain_rx_polarity_flip_physical{81.0}=0x1 + + +dport_map_port_17=1 +dport_map_port_18=2 +dport_map_port_19=3 +dport_map_port_20=4 +dport_map_port_21=5 +dport_map_port_22=6 +dport_map_port_23=7 +dport_map_port_24=8 +dport_map_port_25=9 +dport_map_port_26=10 +dport_map_port_27=11 +dport_map_port_28=12 +dport_map_port_35=13 +dport_map_port_36=14 +dport_map_port_37=15 +dport_map_port_38=16 +dport_map_port_39=17 +dport_map_port_40=18 +dport_map_port_41=19 +dport_map_port_42=20 +dport_map_port_43=21 +dport_map_port_44=22 +dport_map_port_45=23 +dport_map_port_46=24 +dport_map_port_3=25 +dport_map_port_4=26 +dport_map_port_5=27 +dport_map_port_6=28 +dport_map_port_7=29 +dport_map_port_8=30 +dport_map_port_9=31 +dport_map_port_10=32 +dport_map_port_11=33 +dport_map_port_12=34 +dport_map_port_13=35 +dport_map_port_14=36 +dport_map_port_47=37 +dport_map_port_48=38 +dport_map_port_49=39 +dport_map_port_50=40 +dport_map_port_51=41 +dport_map_port_52=42 +dport_map_port_53=43 +dport_map_port_54=44 +dport_map_port_55=45 +dport_map_port_56=46 +dport_map_port_57=47 +dport_map_port_58=48 +dport_map_port_33=49 +dport_map_port_34=50 +dport_map_port_59=51 +dport_map_port_60=52 +dport_map_port_1=53 +dport_map_port_15=54 +dport_map_port_2=55 +dport_map_port_16=56 +#dport_map_port_64=57 diff --git a/device/celestica/x86_64-cel_questone_2-r0/default_sku b/device/celestica/x86_64-cel_questone_2-r0/default_sku new file mode 100644 index 000000000000..8f8cd781d1e9 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/default_sku @@ -0,0 +1 @@ +Questone_2A t1 diff --git a/device/celestica/x86_64-cel_questone_2-r0/installer.conf b/device/celestica/x86_64-cel_questone_2-r0/installer.conf new file mode 100644 index 000000000000..f130299ae2e6 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/installer.conf @@ -0,0 +1,4 @@ +CONSOLE_PORT=0xe060 +CONSOLE_DEV=0 +CONSOLE_SPEED=115200 +ONIE_PLATFORM_EXTRA_CMDLINE_LINUX="processor.max_cstate=1 intel_idle.max_cstate=0 intel_iommu=off noirqdebug" diff --git a/device/celestica/x86_64-cel_questone_2-r0/led_proc_init.soc b/device/celestica/x86_64-cel_questone_2-r0/led_proc_init.soc new file mode 100644 index 000000000000..87039cc85ff7 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/led_proc_init.soc @@ -0,0 +1,9 @@ +#Enable all ports +#port all en=1 +#sleep 6 +#linkscan 250000; port xe,ce linkscan=on + +#Load LED +m0 load 0 0x3800 /usr/share/sonic/platform/custom_led.bin +sleep 5 +led auto on; led start diff --git a/device/celestica/x86_64-cel_questone_2-r0/opennsl-postinit.cfg b/device/celestica/x86_64-cel_questone_2-r0/opennsl-postinit.cfg new file mode 100644 index 000000000000..7008c14c0ffc --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/opennsl-postinit.cfg @@ -0,0 +1,3 @@ +linkscan 250000; port xe,ce linkscan=on +sleep 1 +led auto on; led start diff --git a/device/celestica/x86_64-cel_questone_2-r0/pcie.yaml b/device/celestica/x86_64-cel_questone_2-r0/pcie.yaml new file mode 100644 index 000000000000..854528aa7aa6 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/pcie.yaml @@ -0,0 +1,141 @@ +- bus: '00' + dev: '00' + fn: '0' + id: '1980' + name: 'Host bridge: Intel Corporation Atom Processor C3000 Series System Agent (rev + 11)' +- bus: '00' + dev: '04' + fn: '0' + id: 19a1 + name: 'Host bridge: Intel Corporation Atom Processor C3000 Series Error Registers + (rev 11)' +- bus: '00' + dev: '05' + fn: '0' + id: 19a2 + name: 'Generic system peripheral [0807]: Intel Corporation Atom Processor C3000 + Series Root Complex Event Collector (rev 11)' +- bus: '00' + dev: 09 + fn: '0' + id: 19a4 + name: 'PCI bridge: Intel Corporation Atom Processor C3000 Series PCI Express Root + Port #0 (rev 11)' +- bus: '00' + dev: '10' + fn: '0' + id: 19aa + name: 'PCI bridge: Intel Corporation Atom Processor C3000 Series PCI Express Root + Port #6 (rev 11)' +- bus: '00' + dev: '11' + fn: '0' + id: 19ab + name: 'PCI bridge: Intel Corporation Atom Processor C3000 Series PCI Express Root + Port #7 (rev 11)' +- bus: '00' + dev: '12' + fn: '0' + id: 19ac + name: 'System peripheral: Intel Corporation Atom Processor C3000 Series SMBus Contoller + - Host (rev 11)' +- bus: '00' + dev: '14' + fn: '0' + id: 19c2 + name: 'SATA controller: Intel Corporation Atom Processor C3000 Series SATA Controller + 1 (rev 11)' +- bus: '00' + dev: '15' + fn: '0' + id: 19d0 + name: 'USB controller: Intel Corporation Atom Processor C3000 Series USB 3.0 xHCI + Controller (rev 11)' +- bus: '00' + dev: '16' + fn: '0' + id: 19d1 + name: 'PCI bridge: Intel Corporation Atom Processor C3000 Series Integrated LAN + Root Port #0 (rev 11)' +- bus: '00' + dev: '18' + fn: '0' + id: 19d3 + name: 'Communication controller: Intel Corporation Atom Processor C3000 Series ME + HECI 1 (rev 11)' +- bus: '00' + dev: 1a + fn: '0' + id: 19d8 + name: 'Serial controller: Intel Corporation Atom Processor C3000 Series HSUART Controller + (rev 11)' +- bus: '00' + dev: 1a + fn: '1' + id: 19d8 + name: 'Serial controller: Intel Corporation Atom Processor C3000 Series HSUART Controller + (rev 11)' +- bus: '00' + dev: 1a + fn: '2' + id: 19d8 + name: 'Serial controller: Intel Corporation Atom Processor C3000 Series HSUART Controller + (rev 11)' +- bus: '00' + dev: 1c + fn: '0' + id: 19db + name: 'SD Host controller: Intel Corporation Device 19db (rev 11)' +- bus: '00' + dev: 1f + fn: '0' + id: 19dc + name: 'ISA bridge: Intel Corporation Atom Processor C3000 Series LPC or eSPI (rev + 11)' +- bus: '00' + dev: 1f + fn: '2' + id: 19de + name: 'Memory controller: Intel Corporation Atom Processor C3000 Series Power Management + Controller (rev 11)' +- bus: '00' + dev: 1f + fn: '4' + id: 19df + name: 'SMBus: Intel Corporation Atom Processor C3000 Series SMBus controller (rev + 11)' +- bus: '00' + dev: 1f + fn: '5' + id: 19e0 + name: 'Serial bus controller [0c80]: Intel Corporation Atom Processor C3000 Series + SPI Controller (rev 11)' +- bus: '01' + dev: '00' + fn: '0' + id: b770 + name: 'Ethernet controller: Broadcom Inc. and subsidiaries Device b770 (rev 01)' +- bus: '02' + dev: '00' + fn: '0' + id: '7021' + name: 'Memory controller: Xilinx Corporation Device 7021' +- bus: '03' + dev: '00' + fn: '0' + id: '1533' + name: 'Ethernet controller: Intel Corporation I210 Gigabit Network Connection (rev + 03)' +- bus: '04' + dev: '00' + fn: '0' + id: 15c3 + name: 'Ethernet controller: Intel Corporation Ethernet Connection X553 Backplane + (rev 11)' +- bus: '04' + dev: '00' + fn: '1' + id: 15c3 + name: 'Ethernet controller: Intel Corporation Ethernet Connection X553 Backplane + (rev 11)' diff --git a/device/celestica/x86_64-cel_questone_2-r0/platform_asic b/device/celestica/x86_64-cel_questone_2-r0/platform_asic new file mode 100644 index 000000000000..960467652765 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/platform_asic @@ -0,0 +1 @@ +broadcom diff --git a/device/celestica/x86_64-cel_questone_2-r0/plugins/eeprom.py b/device/celestica/x86_64-cel_questone_2-r0/plugins/eeprom.py new file mode 100644 index 000000000000..22895539823f --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/plugins/eeprom.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica DX010 +# +# 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: + from sonic_eeprom import eeprom_tlvinfo +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + + +class board(eeprom_tlvinfo.TlvInfoDecoder): + + def __init__(self, name, path, cpld_root, ro): + self.eeprom_path = "/sys/class/i2c-adapter/i2c-0/0-0056/eeprom" + super(board, self).__init__(self.eeprom_path, 0, '', True) + diff --git a/device/celestica/x86_64-cel_questone_2-r0/plugins/sfputil.py b/device/celestica/x86_64-cel_questone_2-r0/plugins/sfputil.py new file mode 100755 index 000000000000..03882c65ac00 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/plugins/sfputil.py @@ -0,0 +1,312 @@ +#!/usr/bin/env python +# +# Platform-specific SFP transceiver interface for SONiC +# + +try: + import time + from sonic_sfp.sfputilbase import SfpUtilBase + import struct +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + + +class SfpUtil(SfpUtilBase): + """Platform-specific SfpUtil class""" + + PORT_START = 0 + PORT_END = 55 + QSFP_PORT_START = 48 + QSFP_PORT_END = 55 + __xcvr_presence = {} + + EEPROM_OFFSET = 9 + PORT_INFO_PATH = '/sys/class/questone2_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 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 + 1) + 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): + # port_index = 0 , it's path = /sys/bus/i2c/devices/i2c-10/10-0050/eeprom + # port_index = 55, it's path = /sys/bus/i2c/devices/i2c-65/65-0050/eeprom + # so the real offset is 10 + self.port_to_i2cbus_mapping[x] = (x + 1 + self.EEPROM_OFFSET) + self.port_to_eeprom_mapping[x] = eeprom_path.format( + x + 1 + self.EEPROM_OFFSET) + SfpUtilBase.__init__(self) + for x in range(self.PORT_START, self.PORT_END+1): + self.__xcvr_presence[x] = self.get_presence(x) + + def _do_write_file(self, file_handle, offset, value): + file_handle.seek(offset) + file_handle.write(hex(value)) + file_handle.close() + + def get_presence(self, port_num): + + # Check for invalid port_num + if port_num not in 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): + if not self.get_presence(port_num): + return None + + eeprom_raw = [] + eeprom_raw.append("0x00") + + lpmode = False + eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom'.format(port_num + 1 + self.EEPROM_OFFSET) + if port_num >= 49: + try: + with open(eeprom_path, mode="rb", buffering=0) as eeprom: + eeprom.seek(93) + raw = eeprom.read(1) + eeprom.close() + except Exception as err: + return None + + if len(raw) == 0: + return None + eeprom_raw[0] = hex(ord(raw[0]))[2:].zfill(2) + + power_data = int(eeprom_raw[0], 16) + # if lpmod, power-override bit and power-set bit are both setted + # bit0 bit1 + lpmode = power_data & 0x03 != 0 + else: + return None + + return lpmode + + 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 + + if not self.get_presence(port_num): + return False + eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom'.format(port_num + 1 + self.EEPROM_OFFSET) + try: + reg_file = open(eeprom_path, mode="wb+") + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + reg_file.seek(93) + power_raw = reg_file.read(1) + if power_raw is None: + return False + power_data = int(hex(ord(power_raw))[2:].zfill(2), 16) + + if lpmode: + power_data |= 0x03 + else: + power_data &= ~(0x03) + + reg_file.seek(93) + reg_file.write(struct.pack('B', int(power_data))) + 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): + """ + To detect if any transceiver change event happens. + """ + start_ms = time.time() * 1000 + xcvr_change_event_dict = {} + event = False + + while True: + time.sleep(0.5) + for port in range(self.port_start, self.port_end+1): + curr_presence = self.get_presence(port) + if curr_presence != self.__xcvr_presence[port]: + if curr_presence is True: + xcvr_change_event_dict[str(port)] = '1' + self.__xcvr_presence[port] = True + elif curr_presence is False: + xcvr_change_event_dict[str(port)] = '0' + self.__xcvr_presence[port] = False + event = True + + if event is True: + return True, xcvr_change_event_dict + + if timeout: + now_ms = time.time() * 1000 + if (now_ms - start_ms >= timeout): + return True, xcvr_change_event_dict + + def tx_disable(self, port_num, disable): + """ + @param port_num index of physical port + @param disable, True -- disable port tx signal + False -- enable port tx signal + @return True when operation success, False on failure. + """ + TX_DISABLE_BYTE_OFFSET = 86 + if port_num not in range(self.port_start, self.port_end + 1) or type(disable) != bool: + return False + + # QSFP, set eeprom to disable tx + if port_num in self.qsfp_ports: + presence = self.get_presence(port_num) + if not presence: + return True + + disable = b'\x0f' if disable else b'\x00' + # open eeprom + try: + with open(self.port_to_eeprom_mapping[port_num], mode="wb", buffering=0) as sysfsfile: + sysfsfile.seek(TX_DISABLE_BYTE_OFFSET) + sysfsfile.write(bytearray(disable)) + except IOError: + return False + + # SFP, set tx_disable pin + else: + try: + disable = hex(1) if disable else hex(0) + port_name = self.get_port_name(port_num) + reg_file = open( + "/".join([self.PORT_INFO_PATH, port_name, "sfp_txdisable"]), "w") + reg_file.write(disable) + reg_file.close() + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + return True + + def reset_all(self): + result = True + port_sysfs_path = [] + for port in range(self.port_start, self.port_end+1): + if port not in self.qsfp_ports: + continue + + presence = self.get_presence(port) + if not presence: + continue + + try: + port_name = self.get_port_name(port) + sysfs_path = "/".join([self.PORT_INFO_PATH, + port_name, "qsfp_reset"]) + reg_file = open(sysfs_path, "w") + port_sysfs_path.append(sysfs_path) + except IOError as e: + result = False + continue + + self._do_write_file(reg_file, 0, 0) + + time.sleep(1) + + for sysfs_path in port_sysfs_path: + try: + reg_file = open(sysfs_path, "w") + except IOError as e: + result = False + continue + + self._do_write_file(reg_file, 0, 1) + + return result diff --git a/device/celestica/x86_64-cel_questone_2-r0/pmon_daemon_control.json b/device/celestica/x86_64-cel_questone_2-r0/pmon_daemon_control.json new file mode 100644 index 000000000000..32250390cde4 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/pmon_daemon_control.json @@ -0,0 +1,6 @@ +{ + "skip_ledd": true, + "skip_pcied": true, + "skip_fancontrol": true, + "skip_xcvrd_cmis_mgr": true +} diff --git a/device/celestica/x86_64-cel_questone_2-r0/system_health_monitoring_config.json b/device/celestica/x86_64-cel_questone_2-r0/system_health_monitoring_config.json new file mode 100644 index 000000000000..b92b6af89b61 --- /dev/null +++ b/device/celestica/x86_64-cel_questone_2-r0/system_health_monitoring_config.json @@ -0,0 +1,17 @@ +{ + "services_to_ignore": [], + "devices_to_ignore": [ + "asic", + "psu.temperature", + "PSU-2 FAN-1", + "PSU-1 FAN-1" + ], + "user_defined_checkers": [], + "polling_interval": 60, + "led_color": { + "fault": "amber", + "normal": "green", + "booting": "orange_blink" + } +} + diff --git a/platform/broadcom/one-image.mk b/platform/broadcom/one-image.mk index 6c668ee6894d..f7b9fa2af447 100755 --- a/platform/broadcom/one-image.mk +++ b/platform/broadcom/one-image.mk @@ -54,6 +54,7 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \ $(CEL_HALIBURTON_PLATFORM_MODULE) \ $(CEL_SEASTONE2_PLATFORM_MODULE) \ $(CEL_BELGITE_PLATFORM_MODULE) \ + $(CEL_QUESTONE2_PLATFORM_MODULE) \ $(DELTA_AG9032V1_PLATFORM_MODULE) \ $(DELTA_AG9064_PLATFORM_MODULE) \ $(DELTA_AG5648_PLATFORM_MODULE) \ diff --git a/platform/broadcom/platform-modules-cel.mk b/platform/broadcom/platform-modules-cel.mk index b25aeb7c0900..ead788381658 100644 --- a/platform/broadcom/platform-modules-cel.mk +++ b/platform/broadcom/platform-modules-cel.mk @@ -5,12 +5,14 @@ CEL_HALIBURTON_PLATFORM_MODULE_VERSION = 0.9 CEL_SEASTONE2_PLATFORM_MODULE_VERSION = 0.9 CEL_SILVERSTONE_PLATFORM_MODULE_VERSION = 0.9 CEL_BELGITE_PLATFORM_MODULE_VERSION = 0.9 +CEL_QUESTONE2_PLATFORM_MODULE_VERSION = 0.9 export CEL_DX010_PLATFORM_MODULE_VERSION export CEL_HALIBURTON_PLATFORM_MODULE_VERSION export CEL_SEASTONE2_PLATFORM_MODULE_VERSION export CEL_SILVERSTONE_PLATFORM_MODULE_VERSION export CEL_BELGITE_PLATFORM_MODULE_VERSION +export CEL_QUESTONE2_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 @@ -33,3 +35,7 @@ $(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_SILVERSTONE_P CEL_BELGITE_PLATFORM_MODULE = platform-modules-belgite_$(CEL_BELGITE_PLATFORM_MODULE_VERSION)_amd64.deb $(CEL_BELGITE_PLATFORM_MODULE)_PLATFORM = x86_64-cel_belgite-r0 $(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_BELGITE_PLATFORM_MODULE))) + +CEL_QUESTONE2_PLATFORM_MODULE = platform-modules-questone2_$(CEL_QUESTONE2_PLATFORM_MODULE_VERSION)_amd64.deb +$(CEL_QUESTONE2_PLATFORM_MODULE)_PLATFORM = x86_64-cel_questone_2-r0 +$(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_QUESTONE2_PLATFORM_MODULE))) diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/control b/platform/broadcom/sonic-platform-modules-cel/debian/control index 29cb55b3fc4e..86464b4059b9 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/control +++ b/platform/broadcom/sonic-platform-modules-cel/debian/control @@ -30,3 +30,8 @@ Package: platform-modules-belgite Architecture: amd64 Depends: linux-image-5.10.0-23-2-amd64-unsigned Description: kernel modules for platform devices such as led, sfp + +Package: platform-modules-questone2 +Architecture: amd64 +Depends: linux-image-5.10.0-18-2-amd64-unsigned +Description: kernel modules for platform devices such as led, sfp diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2.init b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2.init new file mode 100644 index 000000000000..d83f087f6517 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2.init @@ -0,0 +1,62 @@ +#!/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 questone2 board. +### END INIT INFO + + +case "$1" in +start) + echo -n "Setting up board... " + + # Loads kernel modules + modprobe questone2_baseboard_cpld + modprobe questone2_switchboard + modprobe mc24lc64t + modprobe optoe + modprobe sff_8436_eeprom + + # Add driver to support TLV - EEPROM + echo 24lc64t 0x56 > /sys/bus/i2c/devices/i2c-0/new_device + + sleep 2 + # One platform support two hwskus, auto select the correct hwsku + # If label is not either Questone-II or Questone-IIA, then Questone-IIA is used by default + hwsku_file="/usr/share/sonic/device/x86_64-cel_questone_2-r0/default_sku" + hwsku=$(decode-syseeprom | grep "Label" | awk '{print $5}') + platform_path="/usr/share/sonic/device/x86_64-cel_questone_2-r0/" + if [ x$hwsku = x"Questone-II" ];then + echo "Questone_2 t1" > $hwsku_file + cp ${platform_path}/Questone_2/platform_components.json $platform_path + cp ${platform_path}/Questone_2/custom_led.bin $platform_path + else + echo "Questone_2A t1" > $hwsku_file + cp ${platform_path}/Questone_2A/platform_components.json $platform_path + cp ${platform_path}/Questone_2A/custom_led.bin $platform_path + fi + + echo "done." + ;; + +stop) + echo "done." + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/platform-modules-questone2 {start|stop}" + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2.install b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2.install new file mode 100644 index 000000000000..f3db4604debd --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2.install @@ -0,0 +1,7 @@ +questone2/cfg/questone2-modules.conf etc/modules-load.d +questone2/cfg/questone2-modprobe.conf etc/modprobe.d +questone2/systemd/platform-modules-questone2.service lib/systemd/system +questone2/cfg/pid_config_questone2.ini usr/local/etc +questone2/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/x86_64-cel_questone_2-r0 +services/platform_api/platform_api_mgnt.sh usr/local/bin +services/platform_api/ps_mem.py usr/local/bin diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2.postinst b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2.postinst new file mode 100644 index 000000000000..7a7371851fd0 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2.postinst @@ -0,0 +1,6 @@ +depmod -a + +/usr/local/bin/platform_api_mgnt.sh install + +systemctl enable platform-modules-questone2.service +systemctl start platform-modules-questone2.service diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/rules b/platform/broadcom/sonic-platform-modules-cel/debian/rules index 29b2b7913ade..f7620a588c9f 100755 --- a/platform/broadcom/sonic-platform-modules-cel/debian/rules +++ b/platform/broadcom/sonic-platform-modules-cel/debian/rules @@ -6,7 +6,7 @@ export KBUILD_EXTRA_SYMBOLS := /sonic/platform/pddf/i2c/Module.symvers.PDDF KVERSION ?= $(shell uname -r) KERNEL_SRC := /lib/modules/$(KVERSION) MOD_SRC_DIR:= $(shell pwd) -MODULE_DIRS:= dx010 haliburton silverstone seastone2 belgite +MODULE_DIRS:= dx010 haliburton silverstone seastone2 belgite questone2 %: dh $@ diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/cfg/pid_config_questone2.ini b/platform/broadcom/sonic-platform-modules-cel/questone2/cfg/pid_config_questone2.ini new file mode 100644 index 000000000000..9ef3d2ce21d2 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/cfg/pid_config_questone2.ini @@ -0,0 +1,19 @@ +#[PID thermal control setting] +[PID enable] +PID_enable=0 + +[SWITCH_TEMP] +setpoint = 95 +P = 3 +I = 0.5 +D = 0.5 +min_output = 76 + +[CPU_TEMP] +setpoint = -15 +P = 3 +I = 0.5 +D = 0.5 +min_output = 76 + + diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/cfg/questone2-modprobe.conf b/platform/broadcom/sonic-platform-modules-cel/questone2/cfg/questone2-modprobe.conf new file mode 100644 index 000000000000..57a28ea72eac --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/cfg/questone2-modprobe.conf @@ -0,0 +1 @@ +options switchboard_fpga allow_unsafe_i2c_access=1 \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/cfg/questone2-modules.conf b/platform/broadcom/sonic-platform-modules-cel/questone2/cfg/questone2-modules.conf new file mode 100644 index 000000000000..574c48f7a66f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/cfg/questone2-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 +8021q + diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/classes/__init__.py b/platform/broadcom/sonic-platform-modules-cel/questone2/classes/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/modules/Makefile b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/Makefile new file mode 100644 index 000000000000..23f793dc3e9f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/Makefile @@ -0,0 +1 @@ +obj-m := mc24lc64t.o questone2_switchboard.o questone2_baseboard_cpld.o sff_8436_eeprom.o optoe.o diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/modules/mc24lc64t.c b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/mc24lc64t.c new file mode 100644 index 000000000000..ae79770a4d8e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/mc24lc64t.c @@ -0,0 +1,173 @@ +/* + * 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; + 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"); diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/modules/optoe.c b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/optoe.c new file mode 100644 index 000000000000..a16bdfc96a06 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/optoe.c @@ -0,0 +1,1147 @@ +/* + * optoe.c - A driver to read and write the EEPROM on optical transceivers + * (SFP, QSFP and similar I2C based devices) + * + * Copyright (C) 2014 Cumulus networks Inc. + * Copyright (C) 2017 Finisar Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Freeoftware Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * Description: + * a) Optical transceiver EEPROM read/write transactions are just like + * the at24 eeproms managed by the at24.c i2c driver + * b) The register/memory layout is up to 256 128 byte pages defined by + * a "pages valid" register and switched via a "page select" + * register as explained in below diagram. + * c) 256 bytes are mapped at a time. 'Lower page 00h' is the first 128 + * bytes of address space, and always references the same + * location, independent of the page select register. + * All mapped pages are mapped into the upper 128 bytes + * (offset 128-255) of the i2c address. + * d) Devices with one I2C address (eg QSFP) use I2C address 0x50 + * (A0h in the spec), and map all pages in the upper 128 bytes + * of that address. + * e) Devices with two I2C addresses (eg SFP) have 256 bytes of data + * at I2C address 0x50, and 256 bytes of data at I2C address + * 0x51 (A2h in the spec). Page selection and paged access + * only apply to this second I2C address (0x51). + * e) The address space is presented, by the driver, as a linear + * address space. For devices with one I2C client at address + * 0x50 (eg QSFP), offset 0-127 are in the lower + * half of address 50/A0h/client[0]. Offset 128-255 are in + * page 0, 256-383 are page 1, etc. More generally, offset + * 'n' resides in page (n/128)-1. ('page -1' is the lower + * half, offset 0-127). + * f) For devices with two I2C clients at address 0x50 and 0x51 (eg SFP), + * the address space places offset 0-127 in the lower + * half of 50/A0/client[0], offset 128-255 in the upper + * half. Offset 256-383 is in the lower half of 51/A2/client[1]. + * Offset 384-511 is in page 0, in the upper half of 51/A2/... + * Offset 512-639 is in page 1, in the upper half of 51/A2/... + * Offset 'n' is in page (n/128)-3 (for n > 383) + * + * One I2c addressed (eg QSFP) Memory Map + * + * 2-Wire Serial Address: 1010000x + * + * Lower Page 00h (128 bytes) + * ===================== + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * |Page Select Byte(127)| + * ===================== + * | + * | + * | + * | + * V + * ------------------------------------------------------------ + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * V V V V + * ------------ -------------- --------------- -------------- + * | | | | | | | | + * | Upper | | Upper | | Upper | | Upper | + * | Page 00h | | Page 01h | | Page 02h | | Page 03h | + * | | | (Optional) | | (Optional) | | (Optional | + * | | | | | | | for Cable | + * | | | | | | | Assemblies) | + * | ID | | AST | | User | | | + * | Fields | | Table | | EEPROM Data | | | + * | | | | | | | | + * | | | | | | | | + * | | | | | | | | + * ------------ -------------- --------------- -------------- + * + * The SFF 8436 (QSFP) spec only defines the 4 pages described above. + * In anticipation of future applications and devices, this driver + * supports access to the full architected range, 256 pages. + * + **/ + +/* #define DEBUG 1 */ + +#undef EEPROM_CLASS +#ifdef CONFIG_EEPROM_CLASS +#define EEPROM_CLASS +#endif +#ifdef CONFIG_EEPROM_CLASS_MODULE +#define EEPROM_CLASS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef EEPROM_CLASS +#include +#endif + +#include + +/* The maximum length of a port name */ +#define MAX_PORT_NAME_LEN 20 + +struct optoe_platform_data { + u32 byte_len; /* size (sum of all addr) */ + u16 page_size; /* for writes */ + u8 flags; + void *dummy1; /* backward compatibility */ + void *dummy2; /* backward compatibility */ + +#ifdef EEPROM_CLASS + struct eeprom_platform_data *eeprom_data; +#endif + char port_name[MAX_PORT_NAME_LEN]; +}; + +/* fundamental unit of addressing for EEPROM */ +#define OPTOE_PAGE_SIZE 128 +/* + * Single address devices (eg QSFP) have 256 pages, plus the unpaged + * low 128 bytes. If the device does not support paging, it is + * only 2 'pages' long. + */ +#define OPTOE_ARCH_PAGES 256 +#define ONE_ADDR_EEPROM_SIZE ((1 + OPTOE_ARCH_PAGES) * OPTOE_PAGE_SIZE) +#define ONE_ADDR_EEPROM_UNPAGED_SIZE (2 * OPTOE_PAGE_SIZE) +/* + * Dual address devices (eg SFP) have 256 pages, plus the unpaged + * low 128 bytes, plus 256 bytes at 0x50. If the device does not + * support paging, it is 4 'pages' long. + */ +#define TWO_ADDR_EEPROM_SIZE ((3 + OPTOE_ARCH_PAGES) * OPTOE_PAGE_SIZE) +#define TWO_ADDR_EEPROM_UNPAGED_SIZE (4 * OPTOE_PAGE_SIZE) +#define TWO_ADDR_NO_0X51_SIZE (2 * OPTOE_PAGE_SIZE) + +/* a few constants to find our way around the EEPROM */ +#define OPTOE_PAGE_SELECT_REG 0x7F +#define ONE_ADDR_PAGEABLE_REG 0x02 +#define ONE_ADDR_NOT_PAGEABLE (1<<2) +#define TWO_ADDR_PAGEABLE_REG 0x40 +#define TWO_ADDR_PAGEABLE (1<<4) +#define TWO_ADDR_0X51_REG 92 +#define TWO_ADDR_0X51_SUPP (1<<6) +#define OPTOE_ID_REG 0 +#define OPTOE_READ_OP 0 +#define OPTOE_WRITE_OP 1 +#define OPTOE_EOF 0 /* used for access beyond end of device */ + +struct optoe_data { + struct optoe_platform_data chip; + int use_smbus; + char port_name[MAX_PORT_NAME_LEN]; + + /* + * Lock protects against activities from other Linux tasks, + * but not from changes by other I2C masters. + */ + struct mutex lock; + struct bin_attribute bin; + struct attribute_group attr_group; + + u8 *writebuf; + unsigned int write_max; + + unsigned int num_addresses; + +#ifdef EEPROM_CLASS + struct eeprom_device *eeprom_dev; +#endif + + /* dev_class: ONE_ADDR (QSFP) or TWO_ADDR (SFP) */ + int dev_class; + + struct i2c_client *client[]; +}; + + +/* + * This parameter is to help this driver avoid blocking other drivers out + * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C + * clock, one 256 byte read takes about 1/43 second which is excessive; + * but the 1/170 second it takes at 400 kHz may be quite reasonable; and + * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. + * + * This value is forced to be a power of two so that writes align on pages. + */ +static unsigned int io_limit = OPTOE_PAGE_SIZE; + +/* + * specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +static unsigned int write_timeout = 25; + +/* + * flags to distinguish one-address (QSFP family) from two-address (SFP family) + * If the family is not known, figure it out when the device is accessed + */ +#define ONE_ADDR 1 +#define TWO_ADDR 2 + +static const struct i2c_device_id optoe_ids[] = { + { "optoe1", ONE_ADDR }, + { "optoe2", TWO_ADDR }, + { "sff8436", ONE_ADDR }, + { "24c04", TWO_ADDR }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, optoe_ids); + +/*-------------------------------------------------------------------------*/ +/* + * This routine computes the addressing information to be used for + * a given r/w request. + * + * Task is to calculate the client (0 = i2c addr 50, 1 = i2c addr 51), + * the page, and the offset. + * + * Handles both single address (eg QSFP) and two address (eg SFP). + * For SFP, offset 0-255 are on client[0], >255 is on client[1] + * Offset 256-383 are on the lower half of client[1] + * Pages are accessible on the upper half of client[1]. + * Offset >383 are in 128 byte pages mapped into the upper half + * + * For QSFP, all offsets are on client[0] + * offset 0-127 are on the lower half of client[0] (no paging) + * Pages are accessible on the upper half of client[1]. + * Offset >127 are in 128 byte pages mapped into the upper half + * + * Callers must not read/write beyond the end of a client or a page + * without recomputing the client/page. Hence offset (within page) + * plus length must be less than or equal to 128. (Note that this + * routine does not have access to the length of the call, hence + * cannot do the validity check.) + * + * Offset within Lower Page 00h and Upper Page 00h are not recomputed + */ + +static uint8_t optoe_translate_offset(struct optoe_data *optoe, + loff_t *offset, struct i2c_client **client) +{ + unsigned int page = 0; + + *client = optoe->client[0]; + + /* if SFP style, offset > 255, shift to i2c addr 0x51 */ + if (optoe->dev_class == TWO_ADDR) { + if (*offset > 255) { + /* like QSFP, but shifted to client[1] */ + *client = optoe->client[1]; + *offset -= 256; + } + } + + /* + * if offset is in the range 0-128... + * page doesn't matter (using lower half), return 0. + * offset is already correct (don't add 128 to get to paged area) + */ + if (*offset < OPTOE_PAGE_SIZE) + return page; + + /* note, page will always be positive since *offset >= 128 */ + page = (*offset >> 7)-1; + /* 0x80 places the offset in the top half, offset is last 7 bits */ + *offset = OPTOE_PAGE_SIZE + (*offset & 0x7f); + + return page; /* note also returning client and offset */ +} + +static ssize_t optoe_eeprom_read(struct optoe_data *optoe, + struct i2c_client *client, + char *buf, unsigned int offset, size_t count) +{ + struct i2c_msg msg[2]; + u8 msgbuf[2]; + unsigned long timeout, read_time; + int status, i; + + memset(msg, 0, sizeof(msg)); + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* + * When we have a better choice than SMBus calls, use a + * combined I2C message. Write address; then read up to + * io_limit data bytes. msgbuf is u8 and will cast to our + * needs. + */ + i = 0; + msgbuf[i++] = offset; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + read_time = jiffies; + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_read_i2c_block_data(client, offset, + count, buf); + break; + case I2C_SMBUS_WORD_DATA: + status = i2c_smbus_read_word_data(client, offset); + if (status >= 0) { + buf[0] = status & 0xff; + if (count == 2) + buf[1] = status >> 8; + status = count; + } + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_read_byte_data(client, offset); + if (status >= 0) { + buf[0] = status; + status = count; + } + break; + default: + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + status = count; + } + + dev_dbg(&client->dev, "eeprom read %zu@%d --> %d (%ld)\n", + count, offset, status, jiffies); + + if (status == count) /* happy path */ + return count; + + if (status == -ENXIO) /* no module present */ + return status; + + /* REVISIT: at HZ=100, this is sloooow */ + usleep_range(1000, 2000); + } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; +} + +static ssize_t optoe_eeprom_write(struct optoe_data *optoe, + struct i2c_client *client, + const char *buf, + unsigned int offset, size_t count) +{ + struct i2c_msg msg; + ssize_t status; + unsigned long timeout, write_time; + unsigned int next_page_start; + int i = 0; + + /* write max is at most a page + * (In this driver, write_max is actually one byte!) + */ + if (count > optoe->write_max) + count = optoe->write_max; + + /* shorten count if necessary to avoid crossing page boundary */ + next_page_start = roundup(offset + 1, OPTOE_PAGE_SIZE); + if (offset + count > next_page_start) + count = next_page_start - offset; + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* If we'll use I2C calls for I/O, set up the message */ + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = optoe->writebuf; + + msg.buf[i++] = offset; + memcpy(&msg.buf[i], buf, count); + msg.len = i + count; + break; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + write_time = jiffies; + + switch (optoe->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_write_i2c_block_data(client, + offset, count, buf); + if (status == 0) + status = count; + break; + case I2C_SMBUS_WORD_DATA: + if (count == 2) { + status = i2c_smbus_write_word_data(client, + offset, (u16)((buf[0])|(buf[1] << 8))); + } else { + /* count = 1 */ + status = i2c_smbus_write_byte_data(client, + offset, buf[0]); + } + if (status == 0) + status = count; + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_write_byte_data(client, offset, + buf[0]); + if (status == 0) + status = count; + break; + default: + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + break; + } + + dev_dbg(&client->dev, "eeprom write %zu@%d --> %ld (%lu)\n", + count, offset, (long int) status, jiffies); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + usleep_range(1000, 2000); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + + +static ssize_t optoe_eeprom_update_client(struct optoe_data *optoe, + char *buf, loff_t off, + size_t count, int opcode) +{ + struct i2c_client *client; + ssize_t retval = 0; + uint8_t page = 0; + loff_t phy_offset = off; + int ret = 0; + + page = optoe_translate_offset(optoe, &phy_offset, &client); + dev_dbg(&client->dev, + "%s off %lld page:%d phy_offset:%lld, count:%ld, opcode:%d\n", + __func__, off, page, phy_offset, (long int) count, opcode); + if (page > 0) { + ret = optoe_eeprom_write(optoe, client, &page, + OPTOE_PAGE_SELECT_REG, 1); + if (ret < 0) { + dev_dbg(&client->dev, + "Write page register for page %d failed ret:%d!\n", + page, ret); + return ret; + } + } + + while (count) { + ssize_t status; + + if (opcode == OPTOE_READ_OP) { + status = optoe_eeprom_read(optoe, client, + buf, phy_offset, count); + } else { + status = optoe_eeprom_write(optoe, client, + buf, phy_offset, count); + } + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + buf += status; + phy_offset += status; + count -= status; + retval += status; + } + + + if (page > 0) { + /* return the page register to page 0 (why?) */ + page = 0; + ret = optoe_eeprom_write(optoe, client, &page, + OPTOE_PAGE_SELECT_REG, 1); + if (ret < 0) { + dev_err(&client->dev, + "Restore page register to 0 failed:%d!\n", ret); + /* error only if nothing has been transferred */ + if (retval == 0) + retval = ret; + } + } + return retval; +} + +/* + * Figure out if this access is within the range of supported pages. + * Note this is called on every access because we don't know if the + * module has been replaced since the last call. + * If/when modules support more pages, this is the routine to update + * to validate and allow access to additional pages. + * + * Returns updated len for this access: + * - entire access is legal, original len is returned. + * - access begins legal but is too long, len is truncated to fit. + * - initial offset exceeds supported pages, return OPTOE_EOF (zero) + */ +static ssize_t optoe_page_legal(struct optoe_data *optoe, + loff_t off, size_t len) +{ + struct i2c_client *client = optoe->client[0]; + u8 regval; + int status; + size_t maxlen; + + if (off < 0) + return -EINVAL; + if (optoe->dev_class == TWO_ADDR) { + /* SFP case */ + /* if only using addr 0x50 (first 256 bytes) we're good */ + if ((off + len) <= TWO_ADDR_NO_0X51_SIZE) + return len; + /* if offset exceeds possible pages, we're not good */ + if (off >= TWO_ADDR_EEPROM_SIZE) + return OPTOE_EOF; + /* in between, are pages supported? */ + status = optoe_eeprom_read(optoe, client, ®val, + TWO_ADDR_PAGEABLE_REG, 1); + if (status < 0) + return status; /* error out (no module?) */ + if (regval & TWO_ADDR_PAGEABLE) { + /* Pages supported, trim len to the end of pages */ + maxlen = TWO_ADDR_EEPROM_SIZE - off; + } else { + /* pages not supported, trim len to unpaged size */ + if (off >= TWO_ADDR_EEPROM_UNPAGED_SIZE) + return OPTOE_EOF; + + /* will be accessing addr 0x51, is that supported? */ + /* byte 92, bit 6 implies DDM support, 0x51 support */ + status = optoe_eeprom_read(optoe, client, ®val, + TWO_ADDR_0X51_REG, 1); + if (status < 0) + return status; + if (regval & TWO_ADDR_0X51_SUPP) { + /* addr 0x51 is OK */ + maxlen = TWO_ADDR_EEPROM_UNPAGED_SIZE - off; + } else { + /* addr 0x51 NOT supported, trim to 256 max */ + if (off >= TWO_ADDR_NO_0X51_SIZE) + return OPTOE_EOF; + maxlen = TWO_ADDR_NO_0X51_SIZE - off; + } + } + len = (len > maxlen) ? maxlen : len; + dev_dbg(&client->dev, + "page_legal, SFP, off %lld len %ld\n", + off, (long int) len); + } else { + /* QSFP case */ + /* if no pages needed, we're good */ + if ((off + len) <= ONE_ADDR_EEPROM_UNPAGED_SIZE) + return len; + /* if offset exceeds possible pages, we're not good */ + if (off >= ONE_ADDR_EEPROM_SIZE) + return OPTOE_EOF; + /* in between, are pages supported? */ + status = optoe_eeprom_read(optoe, client, ®val, + ONE_ADDR_PAGEABLE_REG, 1); + if (status < 0) + return status; /* error out (no module?) */ + if (regval & ONE_ADDR_NOT_PAGEABLE) { + /* pages not supported, trim len to unpaged size */ + if (off >= ONE_ADDR_EEPROM_UNPAGED_SIZE) + return OPTOE_EOF; + maxlen = ONE_ADDR_EEPROM_UNPAGED_SIZE - off; + } else { + /* Pages supported, trim len to the end of pages */ + maxlen = ONE_ADDR_EEPROM_SIZE - off; + } + len = (len > maxlen) ? maxlen : len; + dev_dbg(&client->dev, + "page_legal, QSFP, off %lld len %ld\n", + off, (long int) len); + } + return len; +} + +static ssize_t optoe_read_write(struct optoe_data *optoe, + char *buf, loff_t off, size_t len, int opcode) +{ + struct i2c_client *client = optoe->client[0]; + int chunk; + int status = 0; + ssize_t retval; + size_t pending_len = 0, chunk_len = 0; + loff_t chunk_offset = 0, chunk_start_offset = 0; + loff_t chunk_end_offset = 0; + + dev_dbg(&client->dev, + "%s: off %lld len:%ld, opcode:%s\n", + __func__, off, (long int) len, + (opcode == OPTOE_READ_OP) ? "r" : "w"); + if (unlikely(!len)) + return len; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&optoe->lock); + + /* + * Confirm this access fits within the device suppored addr range + */ + status = optoe_page_legal(optoe, off, len); + if ((status == OPTOE_EOF) || (status < 0)) { + mutex_unlock(&optoe->lock); + return status; + } + len = status; + + /* + * For each (128 byte) chunk involved in this request, issue a + * separate call to sff_eeprom_update_client(), to + * ensure that each access recalculates the client/page + * and writes the page register as needed. + * Note that chunk to page mapping is confusing, is different for + * QSFP and SFP, and never needs to be done. Don't try! + */ + pending_len = len; /* amount remaining to transfer */ + retval = 0; /* amount transferred */ + for (chunk = off >> 7; chunk <= (off + len - 1) >> 7; chunk++) { + + /* + * Compute the offset and number of bytes to be read/write + * + * 1. start at an offset not equal to 0 (within the chunk) + * and read/write less than the rest of the chunk + * 2. start at an offset not equal to 0 and read/write the rest + * of the chunk + * 3. start at offset 0 (within the chunk) and read/write less + * than entire chunk + * 4. start at offset 0 (within the chunk), and read/write + * the entire chunk + */ + chunk_start_offset = chunk * OPTOE_PAGE_SIZE; + chunk_end_offset = chunk_start_offset + OPTOE_PAGE_SIZE; + + if (chunk_start_offset < off) { + chunk_offset = off; + if ((off + pending_len) < chunk_end_offset) + chunk_len = pending_len; + else + chunk_len = chunk_end_offset - off; + } else { + chunk_offset = chunk_start_offset; + if (pending_len < OPTOE_PAGE_SIZE) + chunk_len = pending_len; + else + chunk_len = OPTOE_PAGE_SIZE; + } + + dev_dbg(&client->dev, + "sff_r/w: off %lld, len %ld, chunk_start_offset %lld, chunk_offset %lld, chunk_len %ld, pending_len %ld\n", + off, (long int) len, chunk_start_offset, chunk_offset, + (long int) chunk_len, (long int) pending_len); + + /* + * note: chunk_offset is from the start of the EEPROM, + * not the start of the chunk + */ + status = optoe_eeprom_update_client(optoe, buf, + chunk_offset, chunk_len, opcode); + if (status != chunk_len) { + /* This is another 'no device present' path */ + dev_dbg(&client->dev, + "o_u_c: chunk %d c_offset %lld c_len %ld failed %d!\n", + chunk, chunk_offset, (long int) chunk_len, status); + if (status > 0) + retval += status; + if (retval == 0) + retval = status; + break; + } + buf += status; + pending_len -= status; + retval += status; + } + mutex_unlock(&optoe->lock); + + return retval; +} + +static ssize_t optoe_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, + struct device, kobj)); + struct optoe_data *optoe = i2c_get_clientdata(client); + + return optoe_read_write(optoe, buf, off, count, OPTOE_READ_OP); +} + + +static ssize_t optoe_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, + struct device, kobj)); + struct optoe_data *optoe = i2c_get_clientdata(client); + + return optoe_read_write(optoe, buf, off, count, OPTOE_WRITE_OP); +} + +static int optoe_remove(struct i2c_client *client) +{ + struct optoe_data *optoe; + int i; + + optoe = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &optoe->attr_group); + sysfs_remove_bin_file(&client->dev.kobj, &optoe->bin); + + for (i = 1; i < optoe->num_addresses; i++) + i2c_unregister_device(optoe->client[i]); + +#ifdef EEPROM_CLASS + eeprom_device_unregister(optoe->eeprom_dev); +#endif + + kfree(optoe->writebuf); + kfree(optoe); + return 0; +} + +static ssize_t show_dev_class(struct device *dev, + struct device_attribute *dattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + ssize_t count; + + mutex_lock(&optoe->lock); + count = sprintf(buf, "%d\n", optoe->dev_class); + mutex_unlock(&optoe->lock); + + return count; +} + +static ssize_t set_dev_class(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + int dev_class; + + /* + * dev_class is actually the number of i2c addresses used, thus + * legal values are "1" (QSFP class) and "2" (SFP class) + */ + + if (kstrtoint(buf, 0, &dev_class) != 0 || + dev_class < 1 || dev_class > 2) + return -EINVAL; + + mutex_lock(&optoe->lock); + optoe->dev_class = dev_class; + mutex_unlock(&optoe->lock); + + return count; +} + +/* + * if using the EEPROM CLASS driver, we don't report a port_name, + * the EEPROM CLASS drive handles that. Hence all this code is + * only compiled if we are NOT using the EEPROM CLASS driver. + */ +#ifndef EEPROM_CLASS + +static ssize_t show_port_name(struct device *dev, + struct device_attribute *dattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + ssize_t count; + + mutex_lock(&optoe->lock); + count = sprintf(buf, "%s\n", optoe->port_name); + mutex_unlock(&optoe->lock); + + return count; +} + +static ssize_t set_port_name(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct optoe_data *optoe = i2c_get_clientdata(client); + char port_name[MAX_PORT_NAME_LEN]; + + /* no checking, this value is not used except by show_port_name */ + + if (sscanf(buf, "%19s", port_name) != 1) + return -EINVAL; + + mutex_lock(&optoe->lock); + strcpy(optoe->port_name, port_name); + mutex_unlock(&optoe->lock); + + return count; +} + +static DEVICE_ATTR(port_name, 0644, show_port_name, set_port_name); +#endif /* if NOT defined EEPROM_CLASS, the common case */ + +static DEVICE_ATTR(dev_class, 0644, show_dev_class, set_dev_class); + +static struct attribute *optoe_attrs[] = { +#ifndef EEPROM_CLASS + &dev_attr_port_name.attr, +#endif + &dev_attr_dev_class.attr, + NULL, +}; + +static struct attribute_group optoe_attr_group = { + .attrs = optoe_attrs, +}; + +static int optoe_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + int use_smbus = 0; + struct optoe_platform_data chip; + struct optoe_data *optoe; + int num_addresses = 0; + char port_name[MAX_PORT_NAME_LEN]; + + if (client->addr != 0x50) { + dev_dbg(&client->dev, "probe, bad i2c addr: 0x%x\n", + client->addr); + err = -EINVAL; + goto exit; + } + + if (client->dev.platform_data) { + chip = *(struct optoe_platform_data *)client->dev.platform_data; + /* take the port name from the supplied platform data */ +#ifdef EEPROM_CLASS + strncpy(port_name, chip.eeprom_data->label, MAX_PORT_NAME_LEN); +#else + memcpy(port_name, chip.port_name, MAX_PORT_NAME_LEN); +#endif + dev_dbg(&client->dev, + "probe, chip provided, flags:0x%x; name: %s\n", + chip.flags, client->name); + } else { + if (!id->driver_data) { + err = -ENODEV; + goto exit; + } + dev_dbg(&client->dev, "probe, building chip\n"); + strcpy(port_name, "unitialized"); + chip.flags = 0; +#ifdef EEPROM_CLASS + chip.eeprom_data = NULL; +#endif + } + + /* Use I2C operations unless we're stuck with SMBus extensions. */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + use_smbus = I2C_SMBUS_I2C_BLOCK_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + use_smbus = I2C_SMBUS_WORD_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + use_smbus = I2C_SMBUS_BYTE_DATA; + } else { + err = -EPFNOSUPPORT; + goto exit; + } + } + + + /* + * Make room for two i2c clients + */ + num_addresses = 2; + + optoe = kzalloc(sizeof(struct optoe_data) + + num_addresses * sizeof(struct i2c_client *), + GFP_KERNEL); + if (!optoe) { + err = -ENOMEM; + goto exit; + } + + mutex_init(&optoe->lock); + + /* determine whether this is a one-address or two-address module */ + if ((strcmp(client->name, "optoe1") == 0) || + (strcmp(client->name, "sff8436") == 0)) { + /* one-address (eg QSFP) family */ + optoe->dev_class = ONE_ADDR; + chip.byte_len = ONE_ADDR_EEPROM_SIZE; + num_addresses = 1; + } else if ((strcmp(client->name, "optoe2") == 0) || + (strcmp(client->name, "24c04") == 0)) { + /* SFP family */ + optoe->dev_class = TWO_ADDR; + chip.byte_len = TWO_ADDR_EEPROM_SIZE; + } else { /* those were the only two choices */ + err = -EINVAL; + goto exit; + } + + dev_dbg(&client->dev, "dev_class: %d\n", optoe->dev_class); + optoe->use_smbus = use_smbus; + optoe->chip = chip; + optoe->num_addresses = num_addresses; + memcpy(optoe->port_name, port_name, MAX_PORT_NAME_LEN); + + /* + * Export the EEPROM bytes through sysfs, since that's convenient. + * By default, only root should see the data (maybe passwords etc) + */ + sysfs_bin_attr_init(&optoe->bin); + optoe->bin.attr.name = "eeprom"; + optoe->bin.attr.mode = 0444; + optoe->bin.read = optoe_bin_read; + optoe->bin.size = chip.byte_len; + + if (!use_smbus || + (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_WORD_DATA) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + /* + * NOTE: AN-2079 + * Finisar recommends that the host implement 1 byte writes + * only since this module only supports 32 byte page boundaries. + * 2 byte writes are acceptable for PE and Vout changes per + * Application Note AN-2071. + */ + unsigned int write_max = 1; + + optoe->bin.write = optoe_bin_write; + optoe->bin.attr.mode |= 0200; + + if (write_max > io_limit) + write_max = io_limit; + if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) + write_max = I2C_SMBUS_BLOCK_MAX; + optoe->write_max = write_max; + + /* buffer (data + address at the beginning) */ + optoe->writebuf = kmalloc(write_max + 2, GFP_KERNEL); + if (!optoe->writebuf) { + err = -ENOMEM; + goto exit_kfree; + } + } else { + dev_warn(&client->dev, + "cannot write due to controller restrictions."); + } + + optoe->client[0] = client; + + /* SFF-8472 spec requires that the second I2C address be 0x51 */ + if (num_addresses == 2) { + optoe->client[1] = i2c_new_dummy_device(client->adapter, 0x51); + if (!optoe->client[1]) { + dev_err(&client->dev, "address 0x51 unavailable\n"); + err = -EADDRINUSE; + goto err_struct; + } + } + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &optoe->bin); + if (err) + goto err_struct; + + optoe->attr_group = optoe_attr_group; + + err = sysfs_create_group(&client->dev.kobj, &optoe->attr_group); + if (err) { + dev_err(&client->dev, "failed to create sysfs attribute group.\n"); + goto err_struct; + } + +#ifdef EEPROM_CLASS + optoe->eeprom_dev = eeprom_device_register(&client->dev, + chip.eeprom_data); + if (IS_ERR(optoe->eeprom_dev)) { + dev_err(&client->dev, "error registering eeprom device.\n"); + err = PTR_ERR(optoe->eeprom_dev); + goto err_sysfs_cleanup; + } +#endif + + i2c_set_clientdata(client, optoe); + + dev_info(&client->dev, "%zu byte %s EEPROM, %s\n", + optoe->bin.size, client->name, + optoe->bin.write ? "read/write" : "read-only"); + + if (use_smbus == I2C_SMBUS_WORD_DATA || + use_smbus == I2C_SMBUS_BYTE_DATA) { + dev_notice(&client->dev, + "Falling back to %s reads, performance will suffer\n", + use_smbus == I2C_SMBUS_WORD_DATA ? "word" : "byte"); + } + + return 0; + +#ifdef EEPROM_CLASS +err_sysfs_cleanup: + sysfs_remove_group(&client->dev.kobj, &optoe->attr_group); + sysfs_remove_bin_file(&client->dev.kobj, &optoe->bin); +#endif + +err_struct: + if (num_addresses == 2) { + if (optoe->client[1]) + i2c_unregister_device(optoe->client[1]); + } + + kfree(optoe->writebuf); +exit_kfree: + kfree(optoe); +exit: + dev_dbg(&client->dev, "probe error %d\n", err); + + return err; +} + +/*-------------------------------------------------------------------------*/ + +static struct i2c_driver optoe_driver = { + .driver = { + .name = "optoe", + .owner = THIS_MODULE, + }, + .probe = optoe_probe, + .remove = optoe_remove, + .id_table = optoe_ids, +}; + +static int __init optoe_init(void) +{ + + if (!io_limit) { + pr_err("optoe: io_limit must not be 0!\n"); + return -EINVAL; + } + + io_limit = rounddown_pow_of_two(io_limit); + return i2c_add_driver(&optoe_driver); +} +module_init(optoe_init); + +static void __exit optoe_exit(void) +{ + i2c_del_driver(&optoe_driver); +} +module_exit(optoe_exit); + +MODULE_DESCRIPTION("Driver for optical transceiver (SFP, QSFP, ...) EEPROMs"); +MODULE_AUTHOR("DON BOLLINGER "); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/modules/questone2_baseboard_cpld.c b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/questone2_baseboard_cpld.c new file mode 100644 index 000000000000..1a5527f0d691 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/questone2_baseboard_cpld.c @@ -0,0 +1,409 @@ +/* + * seastone2_baseboard_cpld.c - driver for Seastone2 Base Board CPLD + * This driver implement sysfs for CPLD register access using LPC bus. + * Copyright (C) 2017 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "sys_cpld" +/** + * CPLD register address for read and write. + */ +#define VERSION_ADDR 0xA100 +#define SCRATCH_ADDR 0xA101 +#define SYS_LED_ADDR 0xA162 +#define THERMAL_ADDR 0xA176 +#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) +{ + // CPLD register is one byte + mutex_lock(&cpld_data->cpld_lock); + int 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) +{ + // CPLD register is one byte + mutex_lock(&cpld_data->cpld_lock); + int 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); + +/** + * Read all CPLD register in binary mode. + * @return number of byte read. + */ +static ssize_t dump_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + unsigned long i=0; + ssize_t status; + + mutex_lock(&cpld_data->cpld_lock); +begin: + if(i < count){ + buf[i++] = inb(VERSION_ADDR + off); + off++; + msleep(1); + goto begin; + } + status = count; +exit: + mutex_unlock(&cpld_data->cpld_lock); + return status; +} +static BIN_ATTR_RO(dump, CPLD_REGISTER_SIZE); + +/** + * Show system led status - on/off/1k/4k + * @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 ? "4k" : data ==0x01 ? "1k": "on"); +} + +/** + * Set the status of system led - on/off/1k/4k + * @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, "4k")){ + led_status = 0x02; + }else if(sysfs_streq(buf, "1k")){ + 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 bin_attribute *baseboard_cpld_bin_attrs[] = { + &bin_attr_dump, + NULL, +}; + +static struct attribute_group baseboard_cpld_attrs_grp = { + .attrs = baseboard_cpld_attrs, + .bin_attrs = baseboard_cpld_bin_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; + int portid_count; + + 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/Questone2 Baseboard CPLD Driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/modules/questone2_switchboard.c b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/questone2_switchboard.c new file mode 100644 index 000000000000..0519858ea38a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/questone2_switchboard.c @@ -0,0 +1,2211 @@ +/* + * seastone_switchboard.c - driver for seastone2/questone2 Switch board FPGA/CPLD. + * + * Author: Pradchaya Phucharoen + * + * 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. + * + * / + * \--sys + * \--devices + * \--platform + * \--seastone2 + * |--FPGA + * |--CPLD1 + * |--CPLD2 + * \--SFF + * |--QSFP[1..32] + * \--SFP[1..2] + * + */ + +#ifndef TEST_MODE +#define MOD_VERSION "2.0.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; +#ifdef SEASTONE2 +#define CLASS_NAME "seastone2_fpga" +#define DRIVER_NAME "seastone2" +#define FPGA_PCI_NAME "Seastone2_fpga_pci" +#else +#define CLASS_NAME "questone2_fpga" +#define DRIVER_NAME "questone2" +#define FPGA_PCI_NAME "questone2_fpga_pci" +#endif +#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_TXFAULT 2 +#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_TXFAULT 2 +#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 + * + */ + + +#ifdef SEASTONE2 +#define VIRTUAL_I2C_QSFP_PORT 32 +#define VIRTUAL_I2C_SFP_PORT 1 +#define VIRTUAL_I2C_CPLD_PORT 1 +#define VIRTUAL_I2C_POWER_CHIP_PORT 1 +#define VIRTUAL_I2C_CPLD_B_PORT 1 +#define VIRTUAL_I2C_PSU 1 +#define VIRTUAL_I2C_FAN_TRAY 4 +#define VIRTUAL_I2C_POWER_MON 1 +#define VIRTUAL_I2C_LM75 1 + +#define VIRTUAL_I2C_PORT_LENGTH \ + VIRTUAL_I2C_SFP_PORT+VIRTUAL_I2C_QSFP_PORT+VIRTUAL_I2C_POWER_CHIP_PORT+VIRTUAL_I2C_CPLD_PORT+VIRTUAL_I2C_CPLD_B_PORT+VIRTUAL_I2C_PSU+VIRTUAL_I2C_FAN_TRAY+VIRTUAL_I2C_POWER_MON+VIRTUAL_I2C_LM75 + +#define SFF_PORT_TOTAL VIRTUAL_I2C_QSFP_PORT+VIRTUAL_I2C_SFP_PORT +#else +#define VIRTUAL_I2C_SFP_PORT 48 +#define VIRTUAL_I2C_QSFP_PORT 8 +#define VIRTUAL_I2C_CPLD_PORT 1 +#define VIRTUAL_I2C_POWER_CHIP_PORT 1 +#define VIRTUAL_I2C_CPLD_B_PORT 1 +#define VIRTUAL_I2C_PSU 1 +#define VIRTUAL_I2C_FAN_TRAY 4 +#define VIRTUAL_I2C_POWER_MON 1 +#define VIRTUAL_I2C_LM75 1 + +#define VIRTUAL_I2C_PORT_LENGTH \ + VIRTUAL_I2C_SFP_PORT+VIRTUAL_I2C_QSFP_PORT+VIRTUAL_I2C_POWER_CHIP_PORT+VIRTUAL_I2C_CPLD_PORT+VIRTUAL_I2C_CPLD_B_PORT+VIRTUAL_I2C_PSU+VIRTUAL_I2C_FAN_TRAY+VIRTUAL_I2C_POWER_MON+VIRTUAL_I2C_LM75 + +#define SFF_PORT_TOTAL VIRTUAL_I2C_QSFP_PORT+VIRTUAL_I2C_SFP_PORT +#endif + +#define VIRTUAL_I2C_CPLD_INDEX SFF_PORT_TOTAL + +#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 + +#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; +}; + +#ifdef SEASTONE2 +/* 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"},{I2C_MASTER_CH_1,0x72,1,SFP,"SFP2"}, + /* BUS3 CPLD Access via SYSFS */ + {I2C_MASTER_CH_3,0xFF,0,NONE,"CPLD"}, + /* BUS5 POWER CHIP Exported as virtual bus */ + {I2C_MASTER_CH_5,0xFF,0,NONE,"POWER"}, + /* BUS4 CPLD_B */ + {I2C_MASTER_CH_4,0xFF,0,NONE,"CPLD_B"}, + /* BUS6 PSU */ + {I2C_MASTER_CH_6,0xFF,0,NONE,"PSU"}, + /* BUS7 FAN */ + /* Channel 2 is no hardware connected */ + {I2C_MASTER_CH_7,0x77,0,NONE,"FAN5"},{I2C_MASTER_CH_7,0x77,1,NONE,"FAN4"},{I2C_MASTER_CH_7,0x77,3,NONE,"FAN2"},{I2C_MASTER_CH_7,0x77,4,NONE,"FAN1"}, + /* BUS8 POWER MONITOR */ + {I2C_MASTER_CH_8,0xFF,0,NONE,"UCD90120"}, + /* BUS9 LM75 */ + {I2C_MASTER_CH_9,0xFF,0,NONE,"LM75"}, +}; +#else +/* PREDEFINED I2C SWITCH DEVICE TOPOLOGY */ +static struct i2c_switch fpga_i2c_bus_dev[] = { + /* BUS1 SFP Exported as virtual bus */ + {I2C_MASTER_CH_10,0x72,0,SFP,"SFP1"}, {I2C_MASTER_CH_10,0x72,1,SFP,"SFP2"}, {I2C_MASTER_CH_10,0x72,2,SFP,"SFP3"}, {I2C_MASTER_CH_10,0x72,3,SFP,"SFP4"}, + {I2C_MASTER_CH_10,0x72,4,SFP,"SFP5"}, {I2C_MASTER_CH_10,0x72,5,SFP,"SFP6"}, {I2C_MASTER_CH_10,0x72,6,SFP,"SFP7"}, {I2C_MASTER_CH_10,0x72,7,SFP,"SFP8"}, + {I2C_MASTER_CH_10,0x73,0,SFP,"SFP9"}, {I2C_MASTER_CH_10,0x73,1,SFP,"SFP10"},{I2C_MASTER_CH_10,0x73,2,SFP,"SFP11"},{I2C_MASTER_CH_10,0x73,3,SFP,"SFP12"}, + {I2C_MASTER_CH_10,0x73,4,SFP,"SFP13"},{I2C_MASTER_CH_10,0x73,5,SFP,"SFP14"},{I2C_MASTER_CH_10,0x73,6,SFP,"SFP15"},{I2C_MASTER_CH_10,0x73,7,SFP,"SFP16"}, + {I2C_MASTER_CH_10,0x74,0,SFP,"SFP17"},{I2C_MASTER_CH_10,0x74,1,SFP,"SFP18"},{I2C_MASTER_CH_10,0x74,2,SFP,"SFP19"},{I2C_MASTER_CH_10,0x74,3,SFP,"SFP20"}, + {I2C_MASTER_CH_10,0x74,4,SFP,"SFP21"},{I2C_MASTER_CH_10,0x74,5,SFP,"SFP22"},{I2C_MASTER_CH_10,0x74,6,SFP,"SFP23"},{I2C_MASTER_CH_10,0x74,7,SFP,"SFP24"}, + {I2C_MASTER_CH_10,0x75,0,SFP,"SFP25"},{I2C_MASTER_CH_10,0x75,1,SFP,"SFP26"},{I2C_MASTER_CH_10,0x75,2,SFP,"SFP27"},{I2C_MASTER_CH_10,0x75,3,SFP,"SFP28"}, + {I2C_MASTER_CH_10,0x75,4,SFP,"SFP29"},{I2C_MASTER_CH_10,0x75,5,SFP,"SFP30"},{I2C_MASTER_CH_10,0x75,6,SFP,"SFP31"},{I2C_MASTER_CH_10,0x75,7,SFP,"SFP32"}, + {I2C_MASTER_CH_10,0x76,0,SFP,"SFP33"},{I2C_MASTER_CH_10,0x76,1,SFP,"SFP34"},{I2C_MASTER_CH_10,0x76,2,SFP,"SFP35"},{I2C_MASTER_CH_10,0x76,3,SFP,"SFP36"}, + {I2C_MASTER_CH_10,0x76,4,SFP,"SFP37"},{I2C_MASTER_CH_10,0x76,5,SFP,"SFP38"},{I2C_MASTER_CH_10,0x76,6,SFP,"SFP39"},{I2C_MASTER_CH_10,0x76,7,SFP,"SFP40"}, + {I2C_MASTER_CH_10,0x77,0,SFP,"SFP41"},{I2C_MASTER_CH_10,0x77,1,SFP,"SFP42"},{I2C_MASTER_CH_10,0x77,2,SFP,"SFP43"},{I2C_MASTER_CH_10,0x77,3,SFP,"SFP44"}, + {I2C_MASTER_CH_10,0x77,4,SFP,"SFP45"},{I2C_MASTER_CH_10,0x77,5,SFP,"SFP46"},{I2C_MASTER_CH_10,0x77,6,SFP,"SFP47"},{I2C_MASTER_CH_10,0x77,7,SFP,"SFP48"}, + /* BUS2 QSFP28 Exported as virtual bus */ + {I2C_MASTER_CH_2,0x74,4,QSFP,"QSFP1"},{I2C_MASTER_CH_2,0x74,5,QSFP,"QSFP2"},{I2C_MASTER_CH_2,0x74,6,QSFP,"QSFP3"},{I2C_MASTER_CH_2,0x74,7,QSFP,"QSFP4"}, + {I2C_MASTER_CH_2,0x74,0,QSFP,"QSFP5"},{I2C_MASTER_CH_2,0x74,1,QSFP,"QSFP6"},{I2C_MASTER_CH_2,0x74,2,QSFP,"QSFP7"},{I2C_MASTER_CH_2,0x74,3,QSFP,"QSFP8"}, + /* BUS3 CPLD Access via SYSFS */ + {I2C_MASTER_CH_3,0xFF,0,NONE,"CPLD"}, + /* BUS5 POWER CHIP Exported as virtual bus */ + {I2C_MASTER_CH_5,0xFF,0,NONE,"POWER"}, + /* BUS4 CPLD_B */ + {I2C_MASTER_CH_4,0xFF,0,NONE,"CPLD_B"}, + /* BUS6 PSU */ + {I2C_MASTER_CH_6,0xFF,0,NONE,"PSU"}, + /* BUS7 FAN */ + /* Channel 2 is no hardware connected */ + {I2C_MASTER_CH_7,0x77,0,NONE,"FAN5"},{I2C_MASTER_CH_7,0x77,1,NONE,"FAN4"},{I2C_MASTER_CH_7,0x77,3,NONE,"FAN2"},{I2C_MASTER_CH_7,0x77,4,NONE,"FAN1"}, + /* BUS8 UCD90120 */ + {I2C_MASTER_CH_8,0xFF,0,NONE,"UCD90120"}, + /* BUS9 TEMP SENSOR LM75 */ + {I2C_MASTER_CH_9,0xFF,0,NONE,"LM75"} +}; +#endif +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 = NULL, + .data_mmio_start = NULL, + .data_mmio_len = NULL, +}; + +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 + unsigned long 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; + +/** + * [get_fpga_reg_value description] + * @param dev [description] + * @param devattr [description] + * @param buf [description] + * @return [description] + */ +static ssize_t get_fpga_reg_value(struct device *dev, struct device_attribute *devattr, + 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); +} +/** + * [set_fpga_reg_address description] + * @param dev [description] + * @param devattr [description] + * @param buf [description] + * @param count [description] + * @return [description] + */ +static ssize_t set_fpga_reg_address(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + uint32_t addr; + char *last; + + addr = (uint32_t)strtoul(buf,&last,16); + if(addr == 0 && buf == last){ + return -EINVAL; + } + fpga_data->fpga_read_addr = fpga_dev.data_base_addr+addr; + return count; +} +/** + * [get_fpga_scratch description] + * @param dev [description] + * @param devattr [description] + * @param buf [description] + * @return [description] + */ +static ssize_t get_fpga_scratch(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + return sprintf(buf,"0x%8.8x\n", ioread32(fpga_dev.data_base_addr+FPGA_SCRATCH) & 0xffffffff); +} +/** + * [set_fpga_scratch description] + * @param dev [description] + * @param devattr [description] + * @param buf [description] + * @param count [description] + * @return [description] + */ +static ssize_t set_fpga_scratch(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + uint32_t data; + char *last; + data = (uint32_t)strtoul(buf,&last,16); + if(data == 0 && buf == last){ + return -EINVAL; + } + iowrite32(data, fpga_dev.data_base_addr+FPGA_SCRATCH); + return count; +} +/** + * [set_fpga_reg_value description] + * @param dev [description] + * @param devattr [description] + * @param buf [description] + * @return [description] + */ +static ssize_t set_fpga_reg_value(struct device *dev, struct device_attribute *devattr, + 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; + char *last; + + strcpy(clone, buf); + + mutex_lock(&fpga_data->fpga_lock); + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + addr = (uint32_t)strtoul(tok,&last,16); + if(addr == 0 && tok == last){ + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + value = (uint32_t)strtoul(tok,&last,16); + if(value == 0 && tok == last){ + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + mode = 32; + }else{ + mode = (uint32_t)strtoul(tok,&last,10); + if(mode == 0 && tok == last){ + 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; +} + +/** + * Read all FPGA XCVR register in binary mode. + * @param filp [description] + * @param kobj [description] + * @param attr [description] + * @param buf [description] + * @param off [description] + * @param count [description] + * @return [description] + */ +static ssize_t dump_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + unsigned long i=0; + ssize_t status; + u8 read_reg; + + if( off + count > PORT_XCVR_REGISTER_SIZE ){ + return -EINVAL; + } + mutex_lock(&fpga_data->fpga_lock); + while(i < count){ + read_reg = ioread8(fpga_dev.data_base_addr + SFF_PORT_CTRL_BASE + off + i); + buf[i++] = read_reg; + } + status = count; + mutex_unlock(&fpga_data->fpga_lock); + return status; +} + +/** + * Show FPGA port XCVR ready status + * @param dev [description] + * @param attr [description] + * @param buf [description] + * @return [description] + */ +static ssize_t ready_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + 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 BIN_ATTR_RO( dump, PORT_XCVR_REGISTER_SIZE); + +static struct bin_attribute *fpga_bin_attrs[] = { + &bin_attr_dump, + NULL, +}; + +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, + .bin_attrs = fpga_bin_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; +} +struct device_attribute dev_attr_cpld1_getreg = __ATTR(getreg, 0600, cpld1_getreg_show, cpld1_getreg_store); + +static ssize_t cpld1_dump_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + uint8_t data; + 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); + return sprintf(buf,"0x%2.2x\n",data); +} +static ssize_t cpld1_dump_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + uint8_t addr; + char *last; + addr = (uint8_t)strtoul(buf,&last,16); + if(addr == 0 && buf == last){ + return -EINVAL; + } + fpga_data->cpld1_read_addr = addr; + return size; +} +struct device_attribute dev_attr_cpld1_dump = __ATTR(dump,0600,cpld1_dump_show,cpld1_dump_store); + +static ssize_t cpld1_scratch_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + __u8 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 size) +{ + // CPLD register is one byte + __u8 data; + char *last; + int err; + data = (uint8_t)strtoul(buf,&last,16); + if(data == 0 && buf == last){ + return -EINVAL; + } + 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 size; +} +struct device_attribute dev_attr_cpld1_scratch = __ATTR(scratch,0600,cpld1_scratch_show,cpld1_scratch_store); + +static ssize_t cpld1_setreg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + + uint8_t addr,value; + char *tok; + char clone[size]; + char *pclone = clone; + int err; + char *last; + + strcpy(clone, buf); + + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + return sprintf(buf,"ERROR line %d",__LINE__); + } + addr = (uint8_t)strtoul(tok,&last,16); + if(addr == 0 && tok == last){ + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + return sprintf(buf,"ERROR line %d",__LINE__); + } + value = (uint8_t)strtoul(tok,&last,16); + if(value == 0 && tok == last){ + 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 sprintf(buf,"ERROR line %d",__LINE__); + + return size; +} +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_dump.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; +} +struct device_attribute dev_attr_cpld2_getreg = __ATTR(getreg, 0600, cpld2_getreg_show, cpld2_getreg_store); + +static ssize_t cpld2_dump_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + uint8_t data; + 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); + return sprintf(buf,"0x%2.2x\n",data); +} +static ssize_t cpld2_dump_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + // CPLD register is one byte + uint32_t addr; + char *last; + addr = (uint8_t)strtoul(buf,&last,16); + if(addr == 0 && buf == last){ + return -EINVAL; + } + fpga_data->cpld2_read_addr = addr; + return size; +} +struct device_attribute dev_attr_cpld2_dump = __ATTR(dump,0600,cpld2_dump_show,cpld2_dump_store); + +static ssize_t cpld2_scratch_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + __u8 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 size) +{ + // CPLD register is one byte + __u8 data; + char *last; + int err; + + data = (uint8_t)strtoul(buf,&last,16); + if(data == 0 && buf == last){ + 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 size; +} +struct device_attribute dev_attr_cpld2_scratch = __ATTR(scratch,0600,cpld2_scratch_show,cpld2_scratch_store); + +static ssize_t cpld2_setreg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + uint8_t addr,value; + char *tok; + char clone[size]; + char *pclone = clone; + int err; + char *last; + + strcpy(clone, buf); + + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + return -EINVAL; + } + addr = (uint8_t)strtoul(tok,&last,16); + if(addr == 0 && tok == last){ + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + return -EINVAL; + } + value = (uint8_t)strtoul(tok,&last,16); + if(value == 0 && tok == last){ + 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 size; +} +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_dump.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 size) +{ + 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_LPMOD); + else + data = data | ((u32)0x1 << CTRL_LPMOD); + iowrite32(data,fpga_dev.data_base_addr+REGISTER); + status = size; + } + 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 size) +{ + 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_RST); + else + data = data | ((u32)0x1 << CTRL_RST); + iowrite32(data,fpga_dev.data_base_addr+REGISTER); + status = size; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(qsfp_reset); + +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 size) +{ + 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 = size; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(sfp_txdisable); + +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_sfp_txfault.attr, + &dev_attr_sfp_rxlos.attr, + &dev_attr_sfp_modabs.attr, + &dev_attr_sfp_txdisable.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 size) +{ + 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 size; +} +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,0x09,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,0x09,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 size) +{ + 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 size; +} +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 = -ENXIO; + 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<portid; + void __iomem *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; + } + + unsigned int REG_FDR0; + unsigned int REG_CR0; + unsigned int REG_SR0; + unsigned int REG_DR0; + unsigned int REG_ID0; + + unsigned int 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); + + int cnt=0; + + ////[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 + )){ + int 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(;bidblock[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; + } + + int 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;bidblock[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; + } + } + } + } + +Stop: + // [P] + SET_REG_BIT_L(pci_bar+REG_CR0,I2C_CR_BIT_MSTA); + info( "MS STOP"); + +Done: + iowrite8(1<pca9548.master_bus; + unsigned char switch_addr = dev_data->pca9548.switch_addr; + unsigned char channel = dev_data->pca9548.channel; + + // Acquire the master resource. + mutex_lock(&fpga_i2c_master_locks[master_bus-1]); + uint16_t prev_port = fpga_i2c_lasted_access_port[master_bus-1]; + + if(switch_addr != 0xFF){ + // Check lasted access switch address on a master + if((unsigned char)(prev_port >> 8) == switch_addr){ + // check if channel is the same + if((unsigned char)(prev_port & 0x00FF) != channel){ + // set new PCA9548 at switch_addr to current + error= smbus_access(adapter,switch_addr,flags,I2C_SMBUS_WRITE,1 << channel,I2C_SMBUS_BYTE,NULL); + // update lasted port + fpga_i2c_lasted_access_port[master_bus-1] = switch_addr << 8 | channel; + } + }else{ + // reset prev_port PCA9548 chip + error= smbus_access(adapter,(u16)(prev_port >> 8),flags,I2C_SMBUS_WRITE,0x00,I2C_SMBUS_BYTE,NULL); + // set PCA9548 to current channel + error= smbus_access(adapter,switch_addr,flags,I2C_SMBUS_WRITE,1 << channel,I2C_SMBUS_BYTE,NULL); + // 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); + // reset the channel + mutex_unlock(&fpga_i2c_master_locks[master_bus-1]); + 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; + + 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); + kfree_sensitive(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); + + void __iomem *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); + kfree_sensitive(new_adapter); + kfree_sensitive(new_data); + return NULL; + } + + return new_adapter; +}; + +// I/O resource need. +static struct resource seastone2_resources[] = { + { + .start = 0x10000000, + .end = 0x10001000, + .flags = IORESOURCE_MEM, + }, +}; + +static void seastone2_dev_release( struct device * dev) +{ + return; +} + +static struct platform_device seastone2_dev = { + .name = DRIVER_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(seastone2_resources), + .resource = seastone2_resources, + .dev = { + .release = seastone2_dev_release, + } +}; + +/** + * 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) +{ + struct resource *res; + int ret = 0; + int portid_count; + uint8_t cpld1_version, cpld2_version; + uint16_t prev_i2c_switch = 0; + + /* 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]); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + printk(KERN_ERR "Specified Resource Not Available...\n"); + kfree_sensitive(fpga_data); + return -1; + } + + fpga = kobject_create_and_add("FPGA", &pdev->dev.kobj); + if (!fpga){ + kfree_sensitive(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); + kfree_sensitive(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); + kfree_sensitive(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); + kfree_sensitive(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); + kfree_sensitive(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); + kfree_sensitive(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); + kfree_sensitive(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); + kfree_sensitive(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); + kfree_sensitive(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); + struct sff_device_data *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_client_device(i2c_adap, &sff8436_eeprom_info[0]); + }else{ + fpga_data->sff_i2c_clients[portid_count] = i2c_new_client_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; + dev_data = i2c_get_adapdata(fpga_data->i2c_adapter[portid_count]); + unsigned char master_bus = dev_data->pca9548.master_bus; + unsigned char switch_addr = dev_data->pca9548.switch_addr; + unsigned char channel = dev_data->pca9548.channel; + + 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; +} + +#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; + 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+ (unsigned long)fpga_dev.data_mmio_len); + printk(KERN_INFO ""); + uint32_t buff = ioread32(fpga_dev.data_base_addr); + printk(KERN_INFO "FPGA VERSION : %8.8x\n", buff); + fpgafw_init(); + return 0; + +reg_release: + 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) +{ + 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, +}; + + +static struct platform_driver seastone2_drv = { + .probe = seastone2_drv_probe, + .remove = __exit_p(seastone2_drv_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +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); // Correct way to return an error on a pointer + } + 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_unregister(fpgafwclass); // unregister the device class + 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; + if(fpga_dev.data_base_addr == NULL){ + printk(KERN_ALERT "FPGA PCIe device not found!\n"); + return -ENODEV; + } + platform_device_register(&seastone2_dev); + platform_driver_register(&seastone2_drv); + return 0; +} + +void seastone2_exit(void) +{ + platform_driver_unregister(&seastone2_drv); + platform_device_unregister(&seastone2_dev); + pci_unregister_driver(&pci_dev_ops); +} + +module_init(seastone2_init); +module_exit(seastone2_exit); + +MODULE_AUTHOR("Pradchaya P. pphuhcar@celestica.com"); +#ifdef SEASTONE2 +MODULE_DESCRIPTION("Celestica seastone2 platform driver"); +#else +MODULE_DESCRIPTION("Celestica questone2 platform driver"); +#endif +MODULE_VERSION(MOD_VERSION); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/modules/sff-8436.h b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/sff-8436.h new file mode 100644 index 000000000000..3f8f00f4dcd0 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/sff-8436.h @@ -0,0 +1,31 @@ +#ifndef _LINUX_SFF_8436_H +#define _LINUX_SFF_8436_H + +#include +#include + +/* + * As seen through Linux I2C, differences between the most common types of I2C + * memory include: + * - How much memory is available (usually specified in bit)? + * - What write page size does it support? + * - Special flags (read_only, world readable...)? + * + * If you set up a custom eeprom type, please double-check the parameters. + * Especially page_size needs extra care, as you risk data loss if your value + * is bigger than what the chip actually supports! + */ + +struct sff_8436_platform_data { + u32 byte_len; /* size (sum of all addr) */ + u16 page_size; /* for writes */ + u8 flags; +#define SFF_8436_FLAG_READONLY 0x40 /* sysfs-entry will be read-only */ +#define SFF_8436_FLAG_IRUGO 0x20 /* sysfs-entry will be world-readable */ +#define SFF_8436_FLAG_TAKE8ADDR 0x10 /* take always 8 addresses (24c00) */ + + //void (*setup)(struct memory_accessor *, void *context); + //void *context; +}; + +#endif /* _LINUX_SFF_8436_H */ diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/modules/sff_8436_eeprom.c b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/sff_8436_eeprom.c new file mode 100644 index 000000000000..6779ccee5e5e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/modules/sff_8436_eeprom.c @@ -0,0 +1,982 @@ +/* + * sff_8436_eeprom.c - handle most SFF-8436 based QSFP EEPROMs + * + * Copyright (C) 2014 Cumulus networks Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Freeoftware Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * Description: + * a) SFF 8436 based qsfp read/write transactions are just like the at24 eeproms + * b) The register/memory layout is up to 5 128 byte pages defined by a "pages valid" + * register and switched via a "page select" register as explained in below diagram. + * c) 256 bytes are mapped at a time. page 0 is always mapped to the first 128 bytes and + * the other 4 pages are selectively mapped to the second 128 bytes + * + * SFF 8436 based QSFP Memory Map + * + * 2-Wire Serial Address: 1010000x + * + * Lower Page 00h (128 bytes) + * ===================== + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * |Page Select Byte(127)| + * ===================== + * | + * | + * | + * | + * V + * ----------------------------------------------------------------- + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * | | | | + * V V V V + * ------------- ---------------- ----------------- -------------- + * | | | | | | | | + * | Upper | | Upper | | Upper | | Upper | + * | Page 00h | | Page 01h | | Page 02h | | Page 03h | + * | | | (Optional) | | (Optional) | | (Optional | + * | | | | | | | for Cable | + * | | | | | | | Assemblies) | + * | ID | | AST | | User | | | + * | Fields | | Table | | EEPROM Data | | | + * | | | | | | | | + * | | | | | | | | + * | | | | | | | | + * ------------- ---------------- ----------------- -------------- + * + * + **/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sff-8436.h" + +#include +#include + +#define SFF_8436_EEPROM_SIZE 5*128 +#define SFF_8436_MAX_PAGE_COUNT 5 +#define SFF_8436_MMAP_SIZE 256 +#define SFF_8436_PAGE_SELECT_REG 0x7F + +#define SFF_8436_OPTION_4_OFFSET 0xC3 +#define SFF_8436_PAGE_02_PRESENT (1 << 7) /* Memory Page 02 present */ +#define SFF_8436_PAGE_01_PRESENT (1 << 6) /* Memory Page 01 present */ +#define SFF_8436_STATUS_2_OFFSET 0x02 +#define SFF_8436_STATUS_PAGE_03_PRESENT_L (1 << 2) /* Flat Memory:0- Paging, 1- Page 0 only */ + +struct sff_8436_data { + struct sff_8436_platform_data chip; + //struct memory_accessor macc; + int use_smbus; + + /* + * Lock protects against activities from other Linux tasks, + * but not from changes by other I2C masters. + */ + struct mutex lock; + struct bin_attribute bin; + + u8 *writebuf; + unsigned write_max; + + unsigned num_addresses; + + u8 data[SFF_8436_EEPROM_SIZE]; + + struct i2c_client *client[]; +}; + +typedef enum qsfp_opcode { + QSFP_READ_OP = 0, + QSFP_WRITE_OP = 1 +} qsfp_opcode_e; + +/* + * This parameter is to help this driver avoid blocking other drivers out + * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C + * clock, one 256 byte read takes about 1/43 second which is excessive; + * but the 1/170 second it takes at 400 kHz may be quite reasonable; and + * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. + * + * This value is forced to be a power of two so that writes align on pages. + */ +static unsigned io_limit = 128; + +/* + *pecs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +static unsigned write_timeout = 25; + +#define SFF_8436_PAGE_SIZE 128 +#define SFF_8436_SIZE_BYTELEN 5 +#define SFF_8436_SIZE_FLAGS 8 + +#define SFF_8436_BITMASK(x) (BIT(x) - 1) + + +/* create non-zero magic value for given eeprom parameters */ +#define SFF_8436_DEVICE_MAGIC(_len, _flags) \ + ((1 << SFF_8436_SIZE_FLAGS | (_flags)) \ + << SFF_8436_SIZE_BYTELEN | ilog2(_len)) + +static const struct i2c_device_id sff8436_ids[] = { + { "sff8436",SFF_8436_DEVICE_MAGIC(2048 / 8, 0) }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, sff8436_ids); + +/*-------------------------------------------------------------------------*/ +/* + * This routine computes the addressing information to be used for a given r/w request. + * Assumes that sanity checks for offset happened at sysfs-layer. + * Offset within Lower Page 00h and Upper Page 00h are not recomputed + */ +static uint8_t sff_8436_translate_offset(struct sff_8436_data *sff_8436, + loff_t *offset) +{ + unsigned page = 0; + + if (*offset < SFF_8436_MMAP_SIZE) { + return 0; + } + + page = (*offset >> 7)-1; + + if (page > 0 ) { + *offset = 0x80 + (*offset & 0x7f); + } else { + *offset &= 0xff; + } + + return page; +} + +static int sff_8436_read_reg(struct sff_8436_data *sff_8436, + uint8_t reg, uint8_t *val) +{ + int count = 1, i = 0; + struct i2c_client *client = sff_8436->client[0]; + struct i2c_msg msg[2]; + u8 msgbuf[2]; + ssize_t status; + unsigned long timeout, read_time; + + memset(msg, 0, sizeof(msg)); + + /* + * Writes fail if the previous one didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + read_time = jiffies; + switch (sff_8436->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_read_i2c_block_data(client, + reg, count, val); + break; + case I2C_SMBUS_WORD_DATA: + status = i2c_smbus_read_word_data(client, reg); + + if (status >= 0) { + *val = status & 0xff; + status = count; + } + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_read_byte_data(client, reg); + + if (status >= 0) { + *val = status; + status = count; + } + break; + + default: + i = 0; + msgbuf[i++] = reg; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = val; + msg[1].len = count; + + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + status = count; + break; + } + dev_dbg(&client->dev, "read (using smbus %d) %d@%d --> %zd (%ld)\n", + sff_8436->use_smbus, count, reg, status, jiffies); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + msleep(1); + } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; +} + +static int sff_8436_write_reg(struct sff_8436_data *sff_8436, + uint8_t reg, uint8_t val) +{ + uint8_t data[2] = { reg, val }; + int count = 1; + struct i2c_client *client = sff_8436->client[0]; + struct i2c_msg msg; + ssize_t status; + unsigned long timeout, write_time; + + /* + * Writes fail if the previous one didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + write_time = jiffies; + switch (sff_8436->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_write_i2c_block_data(client, + reg, count, &val); + if (status == 0) + status = count; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_write_byte_data(client, reg, val); + + if (status == 0) + status = count; + break; + default: + msg.addr = client->addr; + msg.flags = 0; + msg.len = sizeof(data); + msg.buf = (char *) data; + + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + break; + } + dev_dbg(&client->dev, "write (using smbus %d) %d@%d --> %zd (%ld)\n", + sff_8436->use_smbus, count, reg, status, jiffies); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + msleep(1); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + +static int sff_8436_write_page_reg(struct sff_8436_data *sff_8436, + uint8_t val) +{ + return sff_8436_write_reg(sff_8436, SFF_8436_PAGE_SELECT_REG, val); +} + +static ssize_t sff_8436_eeprom_read(struct sff_8436_data *sff_8436, char *buf, + unsigned offset, size_t count) +{ + struct i2c_msg msg[2]; + u8 msgbuf[2]; + struct i2c_client *client = sff_8436->client[0]; + unsigned long timeout, read_time; + int status, i; + + memset(msg, 0, sizeof(msg)); + + switch (sff_8436->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* + * When we have a better choice than SMBus calls, use a + * combined I2C message. Write address; then read up to + * io_limit data bytes. Note that read page rollover helps us + * here (unlike writes). msgbuf is u8 and will cast to our + * needs. + */ + i = 0; + msgbuf[i++] = offset; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + read_time = jiffies; + + switch (sff_8436->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_read_i2c_block_data(client, offset, + count, buf); + break; + case I2C_SMBUS_WORD_DATA: + status = i2c_smbus_read_word_data(client, offset); + if (status >= 0) { + buf[0] = status & 0xff; + if (count == 2) + buf[1] = status >> 8; + status = count; + } + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_read_byte_data(client, offset); + if (status >= 0) { + buf[0] = status; + status = count; + } + break; + default: + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + status = count; + } + + dev_dbg(&client->dev, "eeprom read %zu@%d --> %d (%ld)\n", + count, offset, status, jiffies); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + msleep(1); + } while (time_before(read_time, timeout)); + + return -ETIMEDOUT; +} + +static ssize_t sff_8436_eeprom_write(struct sff_8436_data *sff_8436, const char *buf, + unsigned offset, size_t count) +{ + struct i2c_client *client = sff_8436->client[0]; + struct i2c_msg msg; + unsigned long timeout, write_time; + unsigned next_page; + int status, i = 0; + + /* write max is at most a page */ + if (count > sff_8436->write_max) + count = sff_8436->write_max; + + /* Never roll over backwards, to the start of this page */ + next_page = roundup(offset + 1, SFF_8436_PAGE_SIZE); + if (offset + count > next_page) + count = next_page - offset; + + switch (sff_8436->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + /*smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + break; + case I2C_SMBUS_WORD_DATA: + /* Check for odd length transaction */ + count = (count == 1) ? 1 : 2; + break; + case I2C_SMBUS_BYTE_DATA: + count = 1; + break; + default: + /* If we'll use I2C calls for I/O, set up the message */ + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = sff_8436->writebuf; + + msg.buf[i++] = offset; + memcpy(&msg.buf[i], buf, count); + msg.len = i + count; + break; + } + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + write_time = jiffies; + + switch (sff_8436->use_smbus) { + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_write_i2c_block_data(client, + offset, count, buf); + if (status == 0) + status = count; + break; + case I2C_SMBUS_WORD_DATA: + if (count == 2) { + status = i2c_smbus_write_word_data( + client,offset,(u16)((buf[0]) | + (buf[1] << 8))); + } else { + /* count = 1 */ + status = i2c_smbus_write_byte_data( + client, offset, buf[0]); + } + if (status == 0) + status = count; + break; + case I2C_SMBUS_BYTE_DATA: + status = i2c_smbus_write_byte_data(client, offset, buf[0]); + if (status == 0) + status = count; + break; + default: + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + break; + } + + dev_dbg(&client->dev, "eeprom write %zu@%d --> %d (%ld)\n", + count, offset, status, jiffies); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + msleep(1); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + +static ssize_t sff_8436_eeprom_update_client(struct sff_8436_data *sff_8436, + loff_t off, size_t count, qsfp_opcode_e opcode) +{ + struct i2c_client *client = sff_8436->client[0]; + ssize_t retval = 0; + u8 page = 0; + loff_t phy_offset = off; + int ret = 0; + + page = sff_8436_translate_offset(sff_8436, &phy_offset); + + dev_dbg(&client->dev, + "sff_8436_eeprom_update_client off %lld page:%d phy_offset:%lld, count:%zu, opcode:%d\n", + off, page, phy_offset, count, opcode); + if (page > 0) { + ret = sff_8436_write_page_reg(sff_8436, page); + if (ret < 0) { + dev_err(&client->dev, + "sff_8436_write_page_reg for page %d failed ret:%d!\n", + page, ret); + return ret; + } + } + + while (count) { + ssize_t status; + + if (opcode == QSFP_READ_OP) { + status = sff_8436_eeprom_read(sff_8436, (char *)(&sff_8436->data[off]), phy_offset, count); + } else { + status = sff_8436_eeprom_write(sff_8436, (char *)(&sff_8436->data[off]), phy_offset, count); + } + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + phy_offset += status; + off += status; + count -= status; + retval += status; + } + + + if (page > 0) { + ret = sff_8436_write_page_reg(sff_8436, 0); + if (ret < 0) { + dev_err(&client->dev, + "sff_8436_write_page_reg for page 0 failed ret:%d!\n", ret); + return ret; + } + } + return retval; +} + +static ssize_t sff_8436_read_write(struct sff_8436_data *sff_8436, + char *buf, loff_t off, size_t len, qsfp_opcode_e opcode) +{ + struct i2c_client *client = sff_8436->client[0]; + u8 page; + u8 refresh_page = 0; + int ret = 0; + u8 val = 0; + int err_timeout = 0; + size_t pending_len = 0, page_len = 0; + loff_t page_offset = 0, page_start_offset = 0; + + if (unlikely(!len)) + return len; + + if (off > SFF_8436_EEPROM_SIZE) + return 0; + + if (off + len > SFF_8436_EEPROM_SIZE) + len = SFF_8436_EEPROM_SIZE - off; + + if (opcode == QSFP_READ_OP) { + memset(sff_8436->data, 0xff, SFF_8436_EEPROM_SIZE); + } else if (opcode == QSFP_WRITE_OP) { + memcpy(&sff_8436->data[off], buf, len); + } + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&sff_8436->lock); + + /* + * Refresh pages which covers the requested data + * from offset to off + len + * Only refresh pages which contain requested bytes + * + */ + + pending_len = len; + + for (page = off >> 7; page <= (off + len - 1) >> 7; page++) { + refresh_page = 0; + switch (page) { + case 0: + /* Lower page 00h */ + refresh_page = 1; + err_timeout = 1; + break; + case 1: + /* Upper page 00h */ + refresh_page = 1; + err_timeout = 1; + break; + case 2: + /* Upper page 01h */ + ret = sff_8436_read_reg(sff_8436, SFF_8436_OPTION_4_OFFSET, &val); + if (ret < 0) { + dev_dbg(&client->dev, + "sff_8436_read_reg for page 01h status failed %d!\n", ret); + goto err; + } + if (val & SFF_8436_PAGE_01_PRESENT) { + refresh_page = 1; + } + break; + case 3: + /* Upper page 02h */ + ret = sff_8436_read_reg(sff_8436, SFF_8436_OPTION_4_OFFSET, &val); + if (ret < 0) { + dev_dbg(&client->dev, + "sff_8436_read_reg for page 02h status failed %d!\n", ret); + goto err; + } + if (val & SFF_8436_PAGE_02_PRESENT) { + refresh_page = 1; + } + break; + case 4: + /* Upper page 03h */ + ret = sff_8436_read_reg(sff_8436, SFF_8436_STATUS_2_OFFSET, &val); + if (ret < 0) { + dev_dbg(&client->dev, + "sff_8436_read_reg for page 03h status failed %d!\n", ret); + goto err; + } + if (!(val & SFF_8436_STATUS_PAGE_03_PRESENT_L)) { + refresh_page = 1; + } + break; + default: + /* Invalid page index */ + dev_err(&client->dev, "Invalid page %d!\n", page); + ret = -EINVAL; + goto err; + } + + if (!refresh_page) { + /* if page is not valid or already refreshed */ + continue; + } + + /* + * Compute the offset and number of bytes to be read/write + * w.r.t requested page + * + * 1. start at offset 0 (within the page), and read/write the entire page + * 2. start at offset 0 (within the page) and read/write less than entire page + * 3. start at an offset not equal to 0 and read/write the rest of the page + * 4. start at an offset not equal to 0 and read/write less than (end of page - offset) + * + */ + page_start_offset = page * SFF_8436_PAGE_SIZE; + + if (page_start_offset < off) { + page_offset = off; + if (off + pending_len < page_start_offset + SFF_8436_PAGE_SIZE) { + page_len = pending_len; + } else { + page_len = SFF_8436_PAGE_SIZE - off; + } + } else { + page_offset = page_start_offset; + if (pending_len > SFF_8436_PAGE_SIZE) { + page_len = SFF_8436_PAGE_SIZE; + } else { + page_len = pending_len; + } + } + + pending_len = pending_len - page_len; + + dev_dbg(&client->dev, + "sff_read off %lld len %zu page_start_offset %lld page_offset %lld page_len %zu pending_len %zu\n", + off, len, page_start_offset, page_offset, page_len, pending_len); + + /* Refresh the data from offset for specified len */ + ret = sff_8436_eeprom_update_client(sff_8436, page_offset, page_len, opcode); + if (ret != page_len) { + if (err_timeout) { + dev_dbg(&client->dev, "sff_8436_update_client for %s page %d page_offset %lld page_len %zu failed %d!\n", + (page ? "Upper" : "Lower"), (page ? (page-1) : page), page_offset, page_len, ret); + goto err; + } else { + dev_err(&client->dev, "sff_8436_update_client for %s page %d page_offset %lld page_len %zu failed %d!\n", + (page ? "Upper" : "Lower"), (page ? (page-1) : page), page_offset, page_len, ret); + } + } + } + mutex_unlock(&sff_8436->lock); + + if (opcode == QSFP_READ_OP) { + memcpy(buf, &sff_8436->data[off], len); + } + return len; + +err: + mutex_unlock(&sff_8436->lock); + + return ret; +} + +static ssize_t sff_8436_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct sff_8436_data *sff_8436 = i2c_get_clientdata(client); + + return sff_8436_read_write(sff_8436, buf, off, count, QSFP_READ_OP); +} + + +static ssize_t sff_8436_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct sff_8436_data *sff_8436 = i2c_get_clientdata(client); + + return sff_8436_read_write(sff_8436, buf, off, count, QSFP_WRITE_OP); +} +/*-------------------------------------------------------------------------*/ + +/* + * This lets other kernel code access the eeprom data. For example, it + * might hold a board's Ethernet address, or board-specific calibration + * data generated on the manufacturing floor. + */ + +// static ssize_t sff_8436_macc_read(struct memory_accessor *macc, char *buf, +// off_t offset, size_t count) +// { +// struct sff_8436_data *sff_8436 = container_of(macc, struct sff_8436_data, macc); + +// return sff_8436_read_write(sff_8436, buf, offset, count, QSFP_READ_OP); +// } + +// static ssize_t sff_8436_macc_write(struct memory_accessor *macc, const char *buf, +// off_t offset, size_t count) +// { +// struct sff_8436_data *sff_8436 = container_of(macc, struct sff_8436_data, macc); + +// return sff_8436_read_write(sff_8436, buf, offset, count, QSFP_WRITE_OP); +// } + +/*-------------------------------------------------------------------------*/ + +static int sff_8436_remove(struct i2c_client *client) +{ + struct sff_8436_data *sff_8436; + + sff_8436 = i2c_get_clientdata(client); + sysfs_remove_bin_file(&client->dev.kobj, &sff_8436->bin); + + kfree(sff_8436->writebuf); + kfree(sff_8436); + return 0; +} +static int sff_8436_eeprom_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + int use_smbus = 0; + struct sff_8436_platform_data chip; + struct sff_8436_data *sff_8436; + kernel_ulong_t magic; + + if (client->dev.platform_data) { + chip = *(struct sff_8436_platform_data *)client->dev.platform_data; + } else { + /* + * SFF-8436 MMAP is 256 bytes long + */ + magic = SFF_8436_DEVICE_MAGIC(2048 / 8, 0); + chip.byte_len = BIT(magic & SFF_8436_BITMASK(SFF_8436_SIZE_BYTELEN)); + magic >>= SFF_8436_SIZE_BYTELEN; + chip.flags = magic & SFF_8436_BITMASK(SFF_8436_SIZE_FLAGS); + /* + * This is slow, but we can't know all eeproms, so we better + * play safe.pecifying custom eeprom-types via platform_data + * is recommended anyhow. + */ + chip.page_size = 1; + + //chip.setup = NULL; + //chip.context = NULL; + } + + if (!is_power_of_2(chip.byte_len)) + dev_warn(&client->dev, + "byte_len looks suspicious (no power of 2)!\n"); + + if (!chip.page_size) { + dev_err(&client->dev, "page_size must not be 0!\n"); + err = -EINVAL; + goto exit; + } + if (!is_power_of_2(chip.page_size)) + dev_warn(&client->dev, + "page_size looks suspicious (no power of 2)!\n"); + + /* Use I2C operations unless we're stuck with SMBus extensions. */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + use_smbus = I2C_SMBUS_I2C_BLOCK_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + use_smbus = I2C_SMBUS_WORD_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + use_smbus = I2C_SMBUS_BYTE_DATA; + } else { + err = -EPFNOSUPPORT; + goto exit; + } + } + + if (!(sff_8436 = kzalloc(sizeof(struct sff_8436_data) + sizeof(struct i2c_client *), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + mutex_init(&sff_8436->lock); + sff_8436->use_smbus = use_smbus; + sff_8436->chip = chip; + + /* + * Export the EEPROM bytes through sysfs, since that's convenient. + * By default, only root should see the data (maybe passwords etc) + */ + sysfs_bin_attr_init(&sff_8436->bin); + sff_8436->bin.attr.name = "eeprom"; + sff_8436->bin.attr.mode = SFF_8436_FLAG_IRUGO; + sff_8436->bin.read = sff_8436_bin_read; + sff_8436->bin.size = SFF_8436_EEPROM_SIZE; + + //sff_8436->macc.read = sff_8436_macc_read; + + if (!use_smbus || + (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_WORD_DATA) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + //unsigned write_max = chip.page_size; + /* + * NOTE: AN-2079 + * Finisar recommends that the host implement 1 byte writes only, + * since this module only supports 32 byte page boundaries. + * 2 byte writes are acceptable for PE and Vout changes per + * Application Note AN-2071. + */ + unsigned write_max = 1; + + //sff_8436->macc.write = sff_8436_macc_write; + + sff_8436->bin.write = sff_8436_bin_write; + sff_8436->bin.attr.mode |= S_IWUSR; + + if (write_max > io_limit) + write_max = io_limit; + if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) + write_max = I2C_SMBUS_BLOCK_MAX; + sff_8436->write_max = write_max; + + /* buffer (data + address at the beginning) */ + sff_8436->writebuf = kmalloc(write_max + 2, GFP_KERNEL); + if (!sff_8436->writebuf) { + err = -ENOMEM; + goto exit_kfree; + } + } else { + dev_warn(&client->dev, + "cannot write due to controller restrictions."); + } + + memset(sff_8436->data, 0xff, SFF_8436_EEPROM_SIZE); + + sff_8436->client[0] = client; + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &sff_8436->bin); + if (err) + goto err_struct; + + i2c_set_clientdata(client, sff_8436); + + dev_info(&client->dev, "%zu byte %s EEPROM, %s\n", + sff_8436->bin.size, client->name, + "read-only"); + + if (use_smbus == I2C_SMBUS_WORD_DATA || + use_smbus == I2C_SMBUS_BYTE_DATA) { + dev_notice(&client->dev, "Falling back to %s reads, " + "performance will suffer\n", use_smbus == + I2C_SMBUS_WORD_DATA ? "word" : "byte"); + } + + //if (chip.setup) + //chip.setup(&sff_8436->macc, chip.context); + + return 0; + +err_sysfs_cleanup: + sysfs_remove_bin_file(&client->dev.kobj, &sff_8436->bin); +err_struct: + kfree(sff_8436->writebuf); +exit_kfree: + kfree(sff_8436); +exit: + dev_dbg(&client->dev, "probe error %d\n", err); + + return err; +} + +/*-------------------------------------------------------------------------*/ + +static struct i2c_driver sff_8436_driver = { + .driver = { + .name = "sff8436", + .owner = THIS_MODULE, + }, + .probe = sff_8436_eeprom_probe, + .remove = sff_8436_remove, + .id_table = sff8436_ids, +}; + +static int __init sff_8436_init(void) +{ + if (!io_limit) { + pr_err("sff_8436: io_limit must not be 0!\n"); + return -EINVAL; + } + + io_limit = rounddown_pow_of_two(io_limit); + return i2c_add_driver(&sff_8436_driver); +} +module_init(sff_8436_init); + +static void __exit sff_8436_exit(void) +{ + i2c_del_driver(&sff_8436_driver); +} +module_exit(sff_8436_exit); + +MODULE_DESCRIPTION("Driver for SFF-8436 based QSFP EEPROMs"); +MODULE_AUTHOR("VIDYA RAVIPATI "); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/setup.py b/platform/broadcom/sonic-platform-modules-cel/questone2/setup.py new file mode 100644 index 000000000000..97df7dca1aa7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/setup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +import os +from setuptools import setup +os.listdir + +setup( + name='sonic_platform', + version='1.0', + description='Module to initialize Celestica B3010 platforms', + + packages=['questone2', 'sonic_platform'], + package_dir={'questone2' : 'classes', + 'sonic_platform': 'sonic_platform'}, +) + diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/__init__.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/__init__.py new file mode 100644 index 000000000000..db1748e44d2c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/__init__.py @@ -0,0 +1,2 @@ +from . import chassis +from . import platform diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/chassis.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/chassis.py new file mode 100644 index 000000000000..09a5724fb882 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/chassis.py @@ -0,0 +1,393 @@ +#!/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 + import time + from sonic_platform_base.chassis_base import ChassisBase + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +NUM_FAN_DRAWER = 4 +NUM_FAN_PER_DRAWER = 2 +NUM_PSU = 2 +NUM_THERMAL = 13 +NUM_SFP = 56 +NUM_COMPONENT = 7 + +SFP_PORT_START = 1 +SFP_PORT_END = 48 +QSFP_PORT_START = 49 +QSFP_PORT_END = 56 + +REBOOT_CAUSE_REG = "0xA106" +TLV_EEPROM_I2C_BUS = 0 +TLV_EEPROM_I2C_ADDR = 56 +BASE_CPLD_PLATFORM = "sys_cpld" +BASE_GETREG_PATH = "/sys/devices/platform/{}/getreg".format(BASE_CPLD_PLATFORM) +IPMI_GET_SYS_STATUS_LED="ipmitool raw 0x3a 0x0b 0x0" +IPMI_SET_SYS_STATUS_LED="ipmitool raw 0x3A 0x0C 0x00 0x03 0x62 {}" + +class Chassis(ChassisBase): + """Platform-specific Chassis class""" + sfp_status_dict={} + + def __init__(self): + ChassisBase.__init__(self) + self._api_helper = APIHelper() + self.sfp_module_initialized = False + self.fan_module_initialized = False + self.__initialize_eeprom() + + #if not self._api_helper.is_host(): + self.__initialize_thermals() + self.__initialize_fan() + self.__initialize_psu() + #else: + self.__initialize_components() + for port_idx in range(0, NUM_SFP): + present = self.get_sfp(port_idx).get_presence() + self.sfp_status_dict[port_idx] = '1' if present else '0' + + def __initialize_sfp(self): + from sonic_platform.sfp import Sfp + for index in range(0, NUM_SFP): + sfp = Sfp(index) + self._sfp_list.append(sfp) + self.sfp_module_initialized = True + + def __initialize_psu(self): + from sonic_platform.psu import Psu + for index in range(0, NUM_PSU): + psu = Psu(index) + self._psu_list.append(psu) + + def __initialize_fan(self): + from sonic_platform.fan import Fan + from sonic_platform.fan_drawer import FanDrawer + for fand_index in range(0, NUM_FAN_DRAWER): + drawer_fan_list=[] + for fan_index in range(0, NUM_FAN_PER_DRAWER): + fan = Fan(fand_index, fan_index) + self._fan_list.append(fan) + drawer_fan_list.append(fan) + fan_drawer = FanDrawer(fand_index, drawer_fan_list) + self._fan_drawer_list.append(fan_drawer) + self.fan_module_initialized = True + + + def __initialize_thermals(self): + from sonic_platform.thermal import Thermal + for index in range(0, NUM_THERMAL): + thermal = Thermal(index) + self._thermal_list.append(thermal) + + def __initialize_eeprom(self): + from sonic_platform.eeprom import Eeprom + self._eeprom = Eeprom(TLV_EEPROM_I2C_BUS, TLV_EEPROM_I2C_ADDR) + + def __initialize_components(self): + from sonic_platform.component import Component + for index in range(0, NUM_COMPONENT): + component = Component(index) + self._component_list.append(component) + + def initizalize_system_led(self): + return True + + def get_status_led(self): + status, color = self._api_helper.run_command(IPMI_GET_SYS_STATUS_LED) + if not status: + return 'N/A' + + if int(color) == 1: + return "amber" + elif int(color) == 4: + return "green" + + return "N/A" + + def set_status_led(self, color): + if color == self.get_status_led(): + return False + + color_val="0xdc" + if color == "green": + color_val="0xdc" + elif color == "amber": + color_val="0xec" + + cmd=IPMI_SET_SYS_STATUS_LED.format(color_val) + status, res = self._api_helper.run_command(cmd) + + if status: + return True + else: + return False + + 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_serial_number(self): + """ + Retrieves the hardware serial number for the chassis + Returns: + A string containing the hardware serial number for this chassis. + """ + return self._eeprom.get_serial() + + 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. + """ + hw_reboot_cause = self._api_helper.get_register_value( + BASE_GETREG_PATH, REBOOT_CAUSE_REG) + + if hw_reboot_cause == "0x99": + reboot_cause = self.REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC + description = 'ASIC Overload Reboot' + elif hw_reboot_cause == "0x88": + reboot_cause = self.REBOOT_CAUSE_THERMAL_OVERLOAD_CPU + description = 'CPU Overload Reboot' + elif hw_reboot_cause == "0x77": + reboot_cause = self.REBOOT_CAUSE_WATCHDOG + description = 'Hardware Watchdog Reset' + elif hw_reboot_cause == "0x55": + reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER + description = 'CPU Cold Reset' + elif hw_reboot_cause == "0x44": + reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE + description = 'CPU Warm Reset' + elif hw_reboot_cause == "0x33": + reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE + description = 'Soft-Set Cold Reset' + elif hw_reboot_cause == "0x22": + reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE + description = 'Soft-Set Warm Reset' + elif hw_reboot_cause == "0x11": + reboot_cause = self.REBOOT_CAUSE_POWER_LOSS + description = 'Power Off Reset' + elif hw_reboot_cause == "0x00": + reboot_cause = self.REBOOT_CAUSE_POWER_LOSS + description = 'Power Cycle Reset' + else: + reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER + description = 'Hardware reason' + + 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 (0-based) index + Args: + index: An integer, the index (0-based) of the sfp to retrieve. + The index should be the sequence of a physical port in a chassis, + starting from 0. + For example, 0 for Ethernet1, 1 for Ethernet2 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: + sfp = self._sfp_list[index] + except IndexError: + sys.stderr.write("SFP index {} out of range (1-{})\n".format( + index, len(self._sfp_list))) + return sfp + + ############################################################## + ####################### 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: + from sonic_platform.watchdog import Watchdog + self._watchdog = Watchdog() + + 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_helper.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.get_serial_number() + + 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 + + ############################################################## + ###################### 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): + - bool: True if call successful, False if not; + - dict: 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. + The known devices's device_id and device_event was defined as table below. + ----------------------------------------------------------------- + device | device_id | device_event | annotate + ----------------------------------------------------------------- + 'fan' '' '0' Fan removed + '1' Fan inserted + + 'sfp' '' '0' Sfp removed + '1' Sfp inserted + '2' I2C bus stuck + '3' Bad eeprom + '4' Unsupported cable + '5' High Temperature + '6' Bad cable + + 'voltage' '' '0' Vout normal + '1' Vout abnormal + -------------------------------------------------------------------- + Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0', '12':'1'}, + 'voltage':{'U20':'0', 'U21':'1'}} + Indicates that: + fan 0 has been removed, fan 2 has been inserted. + sfp 11 has been removed, sfp 12 has been inserted. + monitored voltage U20 became normal, voltage U21 became abnormal. + Note: For sfp, when event 3-6 happened, the module will not be avalaible, + XCVRD shall stop to read eeprom before SFP recovered from error status. + """ + sfp_dict = {} + + SFP_REMOVED = '0' + SFP_INSERTED = '1' + + SFP_PRESENT = True + SFP_ABSENT = False + + start_time = time.time() + time_period = timeout/float(1000) #Convert msecs to secs + + while time.time() < (start_time + time_period) or timeout == 0: + for port_idx in range(0, NUM_SFP): + if self.sfp_status_dict[port_idx] == SFP_REMOVED and \ + self.get_sfp(port_idx).get_presence() == SFP_PRESENT: + sfp_dict[port_idx] = SFP_INSERTED + self.sfp_status_dict[port_idx] = SFP_INSERTED + elif self.sfp_status_dict[port_idx] == SFP_INSERTED and \ + self.get_sfp(port_idx).get_presence() == SFP_ABSENT: + sfp_dict[port_idx] = SFP_REMOVED + self.sfp_status_dict[port_idx] = SFP_REMOVED + + if sfp_dict: + return True, {'sfp':sfp_dict} + + time.sleep(0.5) + + return True, {'sfp':{}} # Timeout diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/component.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/component.py new file mode 100644 index 000000000000..5f941e1aa303 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/component.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Component contains an implementation of SONiC Platform Base API and +# provides the components firmware management function +# +############################################################################# + +import os.path + +try: + #from sonic_platform_base.component_base import ComponentBase + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +COMPONENT_LIST = [ + ("BIOS", "Basic input/output System"), + ("BASE_CPLD", "Base board CPLD"), + ("BMC", "Used for monitoring and managing whole system"), + ("CPU_CPLD", "CPU board CPLD"), + ("SW_CPLD1", "Switch board CPLD 1"), + ("SW_CPLD2", "Switch board CPLD 2"), + ("FPGA", "Field-programmable gate array") +] +NAME_INDEX = 0 +DESCRIPTION_INDEX = 1 + +BASE_CPLD_PLATFORM = "sys_cpld" +SW_CPLD_PLATFORM = "questone2" +PLATFORM_SYSFS_PATH = "/sys/devices/platform/" + +FPGA_GETREG_PATH = "{}/{}/FPGA/getreg".format( + PLATFORM_SYSFS_PATH, SW_CPLD_PLATFORM) +BASE_GETREG_PATH = "{}/{}/getreg".format( + PLATFORM_SYSFS_PATH, BASE_CPLD_PLATFORM) +SW_CPLD1_GETREG_PATH = "{}/{}/CPLD1/getreg".format( + PLATFORM_SYSFS_PATH, SW_CPLD_PLATFORM) +SW_CPLD2_GETREG_PATH = "{}/{}/CPLD2/getreg".format( + PLATFORM_SYSFS_PATH, SW_CPLD_PLATFORM) +BIOS_VER_PATH = "/sys/class/dmi/id/bios_version" +#FAN_CPLD_VER_PATH = "/sys/bus/i2c/drivers/fancpld/66-000d/version" + +BASE_CPLD_VER_REG = "0xA100" +CPU_CPLD_VER_REG = "0xA1E0" +SW_CPLD_VER_REG = "0x00" +#FPGA_VER_REG = "0x00" + +UNKNOWN_VER = "Unknown" +FPGA_UPGRADE_CMD = "fpga_prog {}" +CPLD_UPGRADE_CMD = "ispvm {}" + + +class Component(): + """Platform-specific Component class""" + + DEVICE_TYPE = "component" + + def __init__(self, component_index): + #ComponentBase.__init__(self) + self.index = component_index + self.name = self.get_name() + self._api_helper = APIHelper() + + def __get_fpga_ver(self): + version_raw = self._api_helper.get_register_value( + FPGA_GETREG_PATH, '0x00') + return "{}.{}".format(int(version_raw[2:][:4], 16), int(version_raw[2:][4:], 16)) if version_raw else UNKNOWN_VER + + def __get_cpld_ver(self): + cpld_version_dict = dict() + cpld_ver_info = { + 'BASE_CPLD': self._api_helper.get_register_value(BASE_GETREG_PATH, BASE_CPLD_VER_REG), + 'CPU_CPLD': self._api_helper.get_register_value(BASE_GETREG_PATH, CPU_CPLD_VER_REG), + 'SW_CPLD1': self._api_helper.get_register_value(SW_CPLD1_GETREG_PATH, SW_CPLD_VER_REG), + 'SW_CPLD2': self._api_helper.get_register_value(SW_CPLD2_GETREG_PATH, SW_CPLD_VER_REG), + } + for cpld_name, cpld_ver in cpld_ver_info.items(): + cpld_ver_str = "{}.{}".format(int(cpld_ver[2], 16), int( + cpld_ver[3], 16)) if cpld_ver else UNKNOWN_VER + cpld_version_dict[cpld_name] = cpld_ver_str + + return cpld_version_dict + + def __get_bmc_ver(self): + cmd="ipmitool mc info | grep 'Firmware Revision'" + status, raw_ver=self._api_helper.run_command(cmd) + if status: + bmc_ver=raw_ver.split(':')[-1].strip() + return {"BMC":bmc_ver} + else: + return {"BMC":"N/A"} + + def get_name(self): + """ + Retrieves the name of the component + Returns: + A string containing the name of the component + """ + return COMPONENT_LIST[self.index][NAME_INDEX] + + def get_description(self): + """ + Retrieves the description of the component + Returns: + A string containing the description of the component + """ + return COMPONENT_LIST[self.index][DESCRIPTION_INDEX] + + def get_firmware_version(self): + """ + Retrieves the firmware version of module + Returns: + string: The firmware versions of the module + """ + fw_version_info = { + 'BIOS': self._api_helper.read_one_line_file(BIOS_VER_PATH), + 'FPGA': self.__get_fpga_ver(), + } + fw_version_info.update(self.__get_cpld_ver()) + fw_version_info.update(self.__get_bmc_ver()) + return fw_version_info.get(self.name, UNKNOWN_VER) + + def install_firmware(self, image_path): + """ + Install firmware to module + Args: + image_path: A string, path to firmware image + Returns: + A boolean, True if install successfully, False if not + """ + + install_command = { + "BASE_CPLD": CPLD_UPGRADE_CMD.format(image_path), + "FAN_CPLD": CPLD_UPGRADE_CMD.format(image_path), + "CPU_CPLD": CPLD_UPGRADE_CMD.format(image_path), + "SW_CPLD1": CPLD_UPGRADE_CMD.format(image_path), + "SW_CPLD2": CPLD_UPGRADE_CMD.format(image_path), + "FPGA": FPGA_UPGRADE_CMD.format(image_path), + }.get(self.name, None) + + if not os.path.isfile(str(image_path)) or (install_command is None) or (not self._api_helper.is_host()): + return False + + status = self._api_helper.run_interactive_command(install_command) + return status diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/eeprom.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/eeprom.py new file mode 100644 index 000000000000..4b0afc59872b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/eeprom.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python + +############################################################################# +# 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 Eeprom(eeprom_tlvinfo.TlvInfoDecoder): + + EEPROM_DECODE_HEADLINES = 6 + + def __init__(self, i2c, reg): + self._eeprom_path = "/sys/class/i2c-adapter/i2c-{0}/{0}-00{1}/eeprom".format(i2c, reg) + super(Eeprom, self).__init__(self._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_product(self): + return self._eeprom.get('0x21', "Undefined.") + + def get_pn(self): + return self._eeprom.get('0x22', "Undefined.") + + 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/questone2/sonic_platform/event.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/event.py new file mode 100644 index 000000000000..6f4d93a88e75 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/event.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +############################################################################# + +import os + +from .helper import APIHelper + + +PLATFORM_PATH = "/sys/devices/platform/" +SWITCH_BRD_PLATFORM = "questone2" +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 = 0x10 + + SFP_PORT_START = 1 + SFP_PORT_END = 48 + QSFP_PORT_START = 49 + QSFP_PORT_END = 56 + + def __init__(self, sfp_list): + self.num_sfp = len(sfp_list) + self._api_helper = APIHelper() + self.__initialize_interrupts() + + def __initialize_interrupts(self): + # Initial Interrupt MASK for QSFP, SFP + + 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 + if port_num in range(self.SFP_PORT_START, self.SFP_PORT_END+1): + port_name = "SFP{}".format( + str(port_num - self.SFP_PORT_START + 1)) + port_type = "sfp" + sysfs_prs_file = "{}_modabs".format(port_type) + elif port_num in range(self.QSFP_PORT_START, self.QSFP_PORT_END+1): + port_name = "QSFP{}".format( + str(port_num - self.QSFP_PORT_START + 1)) + 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_helper.write_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_helper.read_txt_file(prs_path)) + return is_present + + def update_port_event_object(self, interrup_devices, port_dict): + for port_idx in interrup_devices: + port_dict[port_idx] = str(self.__is_port_device_present(port_idx)) + return port_dict + + 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_helper.read_txt_file(int_sysfs) + if interrupt_flags != '0x00': + interrupt_devices[i] = 1 + return interrupt_devices + + +class FanEvent: + ''' Listen to insert/remove fan events ''' + + FAN_INSERT_STATE = '1' + FAN_REMOVE_STATE = '0' + + def __init__(self, fan_list): + self.fan_list = fan_list + + def get_fan_state(self): + fan_dict = {} + for idx, fan in enumerate(self.fan_list): + fan_dict[idx] = self.FAN_INSERT_STATE if fan.get_presence( + ) else self.FAN_REMOVE_STATE + return fan_dict + + def check_fan_status(self, cur_fan_dict, fan_dict): + for idx, fan in enumerate(self.fan_list): + presence = fan.get_presence() + if(presence and cur_fan_dict[idx] == self.FAN_REMOVE_STATE): + fan_dict[idx] = self.FAN_INSERT_STATE + elif(not presence and cur_fan_dict[idx] == self.FAN_INSERT_STATE): + fan_dict[idx] = self.FAN_REMOVE_STATE + return fan_dict + + +class VoltageEvent: + ''' Listen to abnormal voltage events ''' + + VOLTAGE_PATH = "ocores-i2c-polling/i2c-71/71-0035/iio:device0" + VOLTAGE_SCALE_SYSFS = "in_voltage_scale" + VOLTAGE_RAW_SYSFS = "in_voltage{}_raw" + VOLTAGE_CONFIG = { + 0: { + 'name': 'XP3R3V_FD', + 'max': 3470, + 'min': 3140 + }, + 1: { + 'name': 'XP3R3V', + 'max': 3470, + 'min': 3140 + }, + 2: { + 'name': 'XP1R82V', + 'max': 1950, + 'min': 1470 + }, + 3: { + 'name': 'XP1R05V', + 'max': 1068, + 'min': 1032 + }, + 4: { + 'name': 'XP1R7V', + 'max': 1785, + 'min': 1615 + }, + 5: { + 'name': 'XP1R2V', + 'max': 1260, + 'min': 1140 + }, + 6: { + 'name': 'XP1R3V', + 'max': 1352, + 'min': 1248 + }, + 7: { + 'name': 'XP1R5V', + 'max': 1580, + 'min': 1430 + }, + 8: { + 'name': 'XP2R5V', + 'max': 2750, + 'min': 2375 + }, + 9: { + 'name': 'XP0R6V_VTT', + 'max': 632, + 'min': 568 + }, + } + VOLTAGE_NORMAL_EVENT = '0' + VOLTAGE_ABNORMAL_EVENT = '1' + + def __init__(self): + self.voltage_path = os.path.join( + PLATFORM_PATH, SWITCH_BRD_PLATFORM, self.VOLTAGE_PATH) + self.voltage_idx = [0, 1, 8] + self._api_helper = APIHelper() + self.voltage_scale = self._get_voltage_scale() + + def _get_voltage_scale(self): + voltage_scale_path = os.path.join( + self.voltage_path, self.VOLTAGE_SCALE_SYSFS) + get_voltage_scale = self._api_helper.read_txt_file(voltage_scale_path) + if get_voltage_scale: + return float(get_voltage_scale) + else: + return 0 + + def get_voltage_state(self): + voltage_mv_dict = {} + for idx in range(0, len(self.VOLTAGE_CONFIG)): + voltage_raw_path = os.path.join( + self.voltage_path, self.VOLTAGE_RAW_SYSFS.format(idx)) + in_voltageX_raw = self._api_helper.read_txt_file(voltage_raw_path) + + voltage_mV = float(in_voltageX_raw) * self.voltage_scale + if idx in self.voltage_idx: + voltage_mV *= 2 + + v_name = self.VOLTAGE_CONFIG[idx]['name'] + max_v = self.VOLTAGE_CONFIG[idx]['max'] + min_v = self.VOLTAGE_CONFIG[idx]['min'] + + voltage_mv_dict[v_name] = self.VOLTAGE_NORMAL_EVENT + if voltage_mV > max_v or voltage_mV < min_v: + voltage_mv_dict[v_name] = self.VOLTAGE_ABNORMAL_EVENT + return voltage_mv_dict + + def check_voltage_status(self, cur_voltage_dict, int_voltage): + voltage_dict = self.get_voltage_state() + for v_name in voltage_dict: + if voltage_dict[v_name] != cur_voltage_dict[v_name]: + int_voltage[v_name] = voltage_dict[v_name] + return int_voltage diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/fan.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/fan.py new file mode 100644 index 000000000000..02636d826f6b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/fan.py @@ -0,0 +1,340 @@ +#!/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 .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +FAN_MAPPING = { + 0: { + "name": 'FAN-1F', + "fru_id": '5', + "get_speed": '0x81', + "set_speed": '0x40', + "led_offset":'0x4', + "presence": "0x00" + }, + 1: { + "name": 'FAN-1R', + "fru_id": '5', + "get_speed": '0x80', + "set_speed": '0x40', + "led_offset":'0x4', + "presence": "0x00" + }, + 2: { + "name": 'FAN-2F', + "fru_id": '6', + "get_speed": '0x83', + "set_speed": '0x44', + "led_offset":'0x5', + "presence": "0x01" + }, + 3: { + "name": 'FAN-2R', + "fru_id": '6', + "get_speed": '0x82', + "set_speed": '0x44', + "led_offset":'0x5', + "presence": "0x01" + }, + 4: { + "name": 'FAN-3F', + "fru_id": '7', + "get_speed": '0x85', + "set_speed": '0x4c', + "led_offset":'0x6', + "presence": "0x02" + }, + 5: { + "name": 'FAN-3R', + "fru_id": '7', + "get_speed": '0x84', + "set_speed": '0x4c', + "led_offset":'0x6', + "presence": "0x02" + }, + 6: { + "name": 'FAN-4F', + "fru_id": '8', + "get_speed": '0x87', + "set_speed": '0x50', + "led_offset":'0x7', + "presence": "0x03" + }, + 7: { + "name": 'FAN-4R', + "fru_id": '8', + "get_speed": '0x86', + "set_speed": '0x50', + "led_offset":'0x7', + "presence": "0x03" + } +} + +PSU_FAN_MAPPING = { + 0:{ + 'speed':'0x8b' + }, + 1:{ + 'speed':'0x8a' + } +} +NULL_VAL = 'N/A' +MAX_RPM_FRONT=23000 +MAX_RPM_REAR=20500 + +IPMI_FAN_DIR="ipmitool fru list {} | grep -E 'F2B|B2F'" +IPMI_GET_SPEED="ipmitool raw 0x04 0x2d {}" +IPMI_SET_SPEED="ipmitool raw 0x3a 0x0c 0x00 0x03 {} {}" +IPMI_GET_PRESENCE="ipmitool raw 0x3a 0x03 0x03 {}" +IPMI_GET_MODEL="ipmitool fru list {} | grep 'Board Part Number'" +IPMI_GET_SERIAL="ipmitool fru list {} | grep 'Board Serial'" +IPMI_GET_PSU_SPEED="ipmitool raw 0x04 0x2d {}" +IPMI_GET_STATUS_LED="ipmitool raw 0x3a 0x0b {}" + +class Fan(FanBase): + """Platform-specific Fan class""" + + def __init__(self, fan_tray_index, fan_index=0, is_psu_fan=False, psu_index=0, psu_fan_direction=NULL_VAL): + self.fan_index = fan_index + self.fan_tray_index = fan_tray_index + self.is_psu_fan = is_psu_fan + self._api_helper = APIHelper() + if self.is_psu_fan: + self.psu_index = psu_index + self.psu_fan_direction = psu_fan_direction + self.index = (self.fan_tray_index * 2) + self.fan_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 + """ + if self.is_psu_fan: + return self.FAN_DIRECTION_INTAKE + + cmd=IPMI_FAN_DIR.format(FAN_MAPPING[self.index]['fru_id']) + status, raw = self._api_helper.run_command(cmd) + if status: + return self.FAN_DIRECTION_INTAKE if 'B2F' in raw else self.FAN_DIRECTION_EXHAUST + else: + return NULL_VAL + + 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) + """ + speed = 0 + max_rpm=0 + if self.is_psu_fan: + cmd=IPMI_GET_PSU_SPEED.format(PSU_FAN_MAPPING[self.psu_index]['speed']) + status, raw = self._api_helper.run_command(cmd) + if status: + speed=int(int(raw.split()[0], 16)*100/22600*100) + else: + max_rpm = MAX_RPM_FRONT if 'F' in self.get_name() else MAX_RPM_REAR + cmd=IPMI_GET_SPEED.format(FAN_MAPPING[self.index]['get_speed']) + status, raw = self._api_helper.run_command(cmd) + if status: + speed=int(int(raw.split()[0], 16)*150/max_rpm*100) + + if speed > 100: + speed = 100 + return speed + + 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: + speed_pc = pwm_target/255*100 + + 0 : when PWM mode is use + pwm : when pwm mode is not use + """ + return self.get_speed() + + 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 10 + + 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 + Notes: + pwm setting mode must set as Manual + manual: systemctl stop fanctrl.service + auto: systemctl start fanctrl.service + """ + + if self.is_psu_fan: + # Not support + return False + + speed_hex = hex(int(float(speed)/100 * 255)) + cmd=IPMI_SET_SPEED.format(FAN_MAPPING[self.index]['set_speed'], speed_hex) + status, raw = self._api_helper.run_command(cmd) + if status: + return True + return False + + 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 + """ + + if self.is_psu_fan: + # Not support + return False + + return True + + + 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: + Output + STATUS_LED_COLOR_GREEN = "green" + STATUS_LED_COLOR_AMBER = "amber" + STATUS_LED_COLOR_RED = "red" + STATUS_LED_COLOR_OFF = "off" + + Input + 0x1: green + 0x2: red + 0x3: off + """ + if self.is_psu_fan: + # Not support + return NULL_VAL + + cmd=IPMI_GET_STATUS_LED.format(FAN_MAPPING[self.index]["led_offset"]) + status, color = self._api_helper.run_command(cmd) + + if not status: + return 'N/A' + + if int(color) == 0: + return self.STATUS_LED_COLOR_OFF + elif int(color) == 1: + return self.STATUS_LED_COLOR_AMBER + elif int(color) == 2: + return self.STATUS_LED_COLOR_GREEN + + return 'N/A' + + + ############################################################## + ###################### Device methods ######################## + ############################################################## + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + fan_name = FAN_MAPPING[self.index]['name'] if not self.is_psu_fan else "PSU-{} FAN-{}".format( + self.psu_index+1, self.fan_index+1) + + return fan_name + + def get_presence(self): + """ + Retrieves the presence of the FAN + Returns: + bool: True if FAN is present, False if not + """ + if self.is_psu_fan: + from sonic_platform.psu import Psu + return Psu(self.psu_index).get_presence() + + cmd=IPMI_GET_PRESENCE.format(FAN_MAPPING[self.index]['presence']) + status, raw = self._api_helper.run_command(cmd) + if status: + return True if '00' in raw else False + else: + return False + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + if self.is_psu_fan: + return 'N/A' + + cmd=IPMI_GET_MODEL.format(FAN_MAPPING[self.index]['fru_id']) + status, raw = self._api_helper.run_command(cmd) + if status: + if raw: + return raw.split()[-1] + else: + return NULL_VAL + else: + return NULL_VAL + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + if self.is_psu_fan: + return 'N/A' + + cmd=IPMI_GET_SERIAL.format(FAN_MAPPING[self.index]['fru_id']) + status, raw = self._api_helper.run_command(cmd) + if status: + if raw: + return raw.split()[-1] + else: + return NULL_VAL + else: + return 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() and self.get_speed() > 0 diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/fan_drawer.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/fan_drawer.py new file mode 100644 index 000000000000..5ef283a105d4 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/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/questone2/sonic_platform/helper.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/helper.py new file mode 100644 index 000000000000..e9fe8a421b4c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/helper.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python + +import os +import struct +import subprocess +from mmap import * +from sonic_py_common.device_info import get_platform_and_hwsku + +SCALE = 16 +BIN_BITS = 8 +EMPTY_STRING = "" +HOST_CHK_CMD = "docker > /dev/null 2>&1" + + +class APIHelper(): + + def __init__(self): + (self.platform, self.hwsku) = get_platform_and_hwsku() + + def get_register_value(self, getreg_path, register): + cmd = "echo {1} > {0}; cat {0}".format(getreg_path, register) + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + return raw_data.strip().decode('UTF-8') if not err else None + + def hex_to_bin(self, ini_string): + return bin(int(ini_string, SCALE)).zfill(BIN_BITS) + + def is_host(self): + return os.system(HOST_CHK_CMD) == 0 + + def pci_get_value(self, resource, offset): + status = True + result = "" + try: + fd = os.open(resource, os.O_RDWR) + mm = mmap(fd, 0) + mm.seek(int(offset)) + read_data_stream = mm.read(4) + result = struct.unpack('I', read_data_stream) + except Exception: + status = False + return status, result + + def run_command(self, cmd): + status = True + result = "" + try: + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err.decode('UTF-8') == '': + result = raw_data.strip().decode('UTF-8') + except Exception: + status = False + return status, result + + def run_interactive_command(self, cmd): + try: + os.system(cmd) + except Exception: + return False + return True + + def read_txt_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.read() + return data.strip() + except IOError: + pass + return None + + def read_one_line_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.readline() + return data.strip() + except IOError: + pass + return None + + def search_file_by_contain(self, directory, search_str, file_start): + for dirpath, dirnames, files in os.walk(directory): + for name in files: + file_path = os.path.join(dirpath, name) + if name.startswith(file_start) and search_str in self._api_helper.read_txt_file(file_path): + return dirpath + return None + + def write_file(self, file_path, data): + try: + with open(file_path, 'w') as fd: + fd.write(str(data)) + return True + except Exception: + pass + return False + + def ipmi_raw(self, netfn, cmd): + status = True + result = "" + try: + cmd = "ipmitool raw {} {}".format(str(netfn), str(cmd)) + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def ipmi_fru_id(self, id, key=None): + status = True + result = "" + try: + cmd = "ipmitool fru print {}".format(str( + id)) if not key else "ipmitool fru print {0} | grep '{1}' ".format(str(id), str(key)) + + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def ipmi_set_ss_thres(self, id, threshold_key, value): + status = True + result = "" + try: + cmd = "ipmitool sensor thresh '{}' {} {}".format( + str(id), str(threshold_key), str(value)) + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def fru_decode_product_serial(self, data): + + if data and data[4] != 00: + start_product_info = ord(data[4]) * 8 + start_format_version = start_product_info + start_product_info = start_format_version + 1 + start_product_Lang_code = start_product_info + 1 + start_product_Manu_name = start_product_Lang_code + 1 + start_product_Manu_name_length = ord(data[start_product_Manu_name]) & 0x0F + start_product_name = start_product_Manu_name + start_product_Manu_name_length + 1 + start_product_name_length = ord(data[start_product_name]) & 0x0F + start_product_module_number = start_product_name + start_product_name_length +1 + start_product_module_number_length = ord(data[start_product_module_number]) & 0x0F + start_product_version = start_product_module_number + start_product_module_number_length +1 + start_product_version_length = ord(data[start_product_version]) & 0x0F + start_product_serial_number = start_product_version + start_product_version_length +1 + start_product_serial_number_length = ord(data[start_product_serial_number]) & 0x1F + return data[start_product_serial_number+1:start_product_serial_number+start_product_serial_number_length+1] + return "N/A" + + def fru_decode_product_model(self, data): + if data and data[4] != 00: + start_product_info = ord(data[4]) * 8 + start_format_version = start_product_info + start_product_info = start_format_version + 1 + start_product_lang_code = start_product_info + 1 + start_product_manu_name = start_product_lang_code + 1 + start_product_manu_name_length = ord(data[start_product_manu_name]) & 0x1F + start_product_name = start_product_manu_name + start_product_manu_name_length + 1 + start_product_name_length = ord(data[start_product_name]) & 0x1F + start_product_module_number = start_product_name + start_product_name_length + 1 + start_product_module_number_length = ord(data[start_product_module_number]) & 0x1F + return data[start_product_module_number + 1: start_product_module_number +start_product_module_number_length + 1] + return "N/A" + + def fru_decode_product_name(self, data): + + if data and data[4] != 00: + start_product_info = ord(data[4]) * 8 + start_format_version = start_product_info + start_product_info = start_format_version + 1 + start_product_Lang_code = start_product_info + 1 + start_product_Manu_name = start_product_Lang_code + 1 + start_product_Manu_name_length = ord(data[start_product_Manu_name]) & 0x0F + start_product_name = start_product_Manu_name + start_product_Manu_name_length + 1 + start_product_name_length = ord(data[start_product_name]) & 0x0F + return data[start_product_name+1: start_product_name+start_product_name_length+1] + + return "N/A" + + def read_eeprom_sysfs(self,sys_path,sysfs_file): + sysfs_path = os.path.join(sys_path, sysfs_file) + try: + with open(sysfs_path, mode='rb', buffering=0) as fd: + data = fd.read(256) + return data + except Exception: + pass + return None diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/pcie.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/pcie.py new file mode 100644 index 000000000000..2f7931d42b01 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/pcie.py @@ -0,0 +1,15 @@ +# +# pcie_base.py +# +# Abstract base class for implementing platform-specific +# PCIE functionality for SONiC +# + +try: + from sonic_platform_base.sonic_pcie.pcie_common import PcieUtil +except ImportError as e: + raise ImportError (str(e) + " - required module not found") + +class Pcie(PcieUtil): + def __init__(self, platform_path): + PcieUtil.__init__(self, platform_path) diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/platform.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/platform.py new file mode 100644 index 000000000000..c9ba822ac0da --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/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/questone2/sonic_platform/psu.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/psu.py new file mode 100644 index 000000000000..4c292b740ebb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/psu.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Module contains an implementation of SONiC Platform Base API and +# provides the PSUs status which are available in the platform +# +############################################################################# + +try: + from sonic_platform_base.psu_base import PsuBase + from sonic_platform.fan import Fan + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +PSU_NUM_FAN = [1, 1] +PSU_INFO_MAPPING = { + 0: { + "name": "PSU-L", + "power_status_offset":3, + "presence_offset":5, + "fru_num":"3", + "power":"PSUL_POut", + "current":"PSUL_COut", + "voltage":"PSUL_VOut", + "led_offset":0x2 + }, + 1: { + "name": "PSU-R", + "power_status_offset":2, + "presence_offset":4, + "fru_num":"4", + "power":"PSUR_POut", + "current":"PSUR_COut", + "voltage":"PSUR_VOut", + "led_offset":0x3 + } +} + +IPMI_RAW="ipmitool raw 0x3a 0x0c 0x0 0x2 0x60" +IPMI_FRU_MODEL="ipmitool fru list {} | grep 'Product Name'" +IPMI_FRU_SERIAL="ipmitool fru list {} | grep 'Product Serial'" +IPMI_SDR="ipmitool sdr | grep {}" +IPMI_GET_STATUS_LED="ipmitool raw 0x3a 0x0b {}" + +class Psu(PsuBase): + """Platform-specific Psu class""" + + def __init__(self, psu_index): + PsuBase.__init__(self) + self.index = psu_index + self._api_helper = APIHelper() + fan = Fan(0, 0, True, self.index, 'N/A') + 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 + """ + psu_voltage = 0.0 + if not self.get_status(): + return psu_voltage + + cmd=IPMI_SDR.format(PSU_INFO_MAPPING[self.index]["voltage"]) + status, voltage = self._api_helper.run_command(cmd) + if status: + return float(voltage.split()[2]) + else: + return psu_voltage + + def get_voltage_high_threshold(self): + return 12.6 + + def get_voltage_low_threshold(self): + return 11.4 + + def get_current(self): + """ + Retrieves present electric current supplied by PSU + Returns: + A float number, the electric current in amperes, e.g 15.4 + """ + psu_current = 0.0 + + if not self.get_status(): + return psu_current + + cmd=IPMI_SDR.format(PSU_INFO_MAPPING[self.index]["current"]) + status, current = self._api_helper.run_command(cmd) + if status: + return float(current.split()[2]) + else: + return psu_current + + + def get_power(self): + """ + Retrieves current energy supplied by PSU + Returns: + A float number, the power in watts, e.g. 302.6 + """ + psu_power = 0.0 + + if not self.get_status(): + return psu_power + + cmd=IPMI_SDR.format(PSU_INFO_MAPPING[self.index]["power"]) + status, power = self._api_helper.run_command(cmd) + if status: + return float(power.split()[2]) + else: + return psu_power + + 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.get_status() + + def set_status_led(self, color): + """ + Sets the state of the PSU status LED + Args: + color: A string representing the color with which to set the PSU status LED + Note: Only support green and off + Returns: + bool: True if status LED state is set successfully, False if not + """ + # NOT ALLOW + + return False + + def get_status_led(self): + """ + Gets the state of the PSU status LED + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + if self.get_presence(): + if self.get_powergood_status(): + return self.STATUS_LED_COLOR_GREEN + else: + return "blinking green" + else: + return self.STATUS_LED_COLOR_OFF + + + ############################################################## + ###################### Device methods ######################## + ############################################################## + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + return PSU_INFO_MAPPING[self.index]["name"] + + def get_presence(self): + """ + Retrieves the presence of the PSU + Returns: + bool: True if PSU is present, False if not + """ + status, psu_presence = self._api_helper.run_command(IPMI_RAW) + if status: + return True if (int(psu_presence,16) >> \ + PSU_INFO_MAPPING[self.index]['presence_offset'] & 1) == 0 else False + else: + return False + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + cmd=IPMI_FRU_MODEL.format(PSU_INFO_MAPPING[self.index]['fru_num']) + status, model= self._api_helper.run_command(cmd) + if status: + return model.split(':')[1] + else: + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + cmd=IPMI_FRU_SERIAL.format(PSU_INFO_MAPPING[self.index]['fru_num']) + status, model= self._api_helper.run_command(cmd) + if status: + return model.split()[-1] + else: + return 'N/A' + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + if not self.get_presence(): + return False + status, psu_status = self._api_helper.run_command(IPMI_RAW) + if status: + return True if (int(psu_status,16) >> \ + PSU_INFO_MAPPING[self.index]['power_status_offset'] & 1) == 1 else False + else: + return False diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/sfp.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/sfp.py new file mode 100644 index 000000000000..af364eaabd8d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/sfp.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Sfp contains an implementation of SONiC Platform Base API and +# provides the sfp device status which are available in the platform +# +############################################################################# +import time + +try: + from sonic_platform_base.sonic_xcvr.sfp_optoe_base import SfpOptoeBase + from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +SFP_TYPE = "SFP" +QSFP_TYPE = "QSFP" + +QSFP_PORT_START = 49 +QSFP_PORT_END = 56 +SFP_PORT_START = 1 +SFP_PORT_END = 48 + +PORT_INFO_PATH = '/sys/class/questone2_fpga' +SFP_I2C_START = 2 + + +class Sfp(SfpOptoeBase): + """Platform-specific Sfp class""" + + # Path to QSFP sysfs + PLATFORM_ROOT_PATH = "/usr/share/sonic/device" + PMON_HWSKU_PATH = "/usr/share/sonic/hwsku" + + def __init__(self, sfp_index): + SfpOptoeBase.__init__(self) + # Init index + self.index = sfp_index + self.port_num = self.index + 1 + self.sfp_type, self.port_name = self.__get_sfp_info() + self._api_helper = APIHelper() + self.platform = self._api_helper.platform + self.hwsku = self._api_helper.hwsku + + # Init eeprom path + self.eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom'.format(SFP_I2C_START+sfp_index) + + def __get_sfp_info(self): + port_name = "Unknown" + sfp_type = "Unknown" + + if self.port_num >= QSFP_PORT_START and self.port_num <= QSFP_PORT_END: + sfp_type = QSFP_TYPE + port_name = "QSFP" + str(self.port_num - QSFP_PORT_START + 1) + elif self.port_num >= SFP_PORT_START and self.port_num <= SFP_PORT_END: + sfp_type = SFP_TYPE + port_name = "SFP" + str(self.port_num) + return sfp_type, port_name + + def __get_path_to_port_config_file(self): + platform_path = "/".join([self.PLATFORM_ROOT_PATH, self.platform]) + hwsku_path = "/".join([platform_path, self.hwsku] + ) if self._api_helper.is_host() else self.PMON_HWSKU_PATH + return "/".join([hwsku_path, "port_config.ini"]) + + def get_eeprom_path(self): + return self.eeprom_path + + def get_reset_status(self): + """ + Retrieves the reset status of SFP + Returns: + A Boolean, True if reset enabled, False if disabled + """ + reg_status = self._api_helper.read_one_line_file( + "/".join([PORT_INFO_PATH, self.port_name, "qsfp_reset"])) + + return (int(reg_status) == 0) + + + def reset(self): + """ + Reset SFP and return all user module settings to their default srate. + Returns: + A boolean, True if successful, False if not + """ + if self.sfp_type != QSFP_TYPE: + return False + + try: + reg_file = open( + "/".join([PORT_INFO_PATH, self.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([PORT_INFO_PATH, self.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 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 + """ + if self.sfp_type != QSFP_TYPE: + return False + + try: + reg_file = open( + "/".join([PORT_INFO_PATH, self.port_name, "qsfp_lpmode"]), "w") + except IOError as e: + return False + + if lpmode: + value=1 + else: + value=0 + reg_file.write(hex(value)) + reg_file.close() + + return True + + 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 + """ + if self.sfp_type != QSFP_TYPE: + return False + + reg_status = self._api_helper.read_one_line_file( + "/".join([PORT_INFO_PATH, self.port_name, "qsfp_lpmode"])) + + return (int(reg_status) == 1) + + def get_position_in_parent(self): + return self.index + + def is_replaceable(self): + return True + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + sfputil_helper = SfpUtilHelper() + sfputil_helper.read_porttab_mappings( + self.__get_path_to_port_config_file()) + name = sfputil_helper.physical_to_logical[self.index] or "Unknown" + return name + + def get_presence(self): + """ + Retrieves the presence of the PSU + Returns: + bool: True if PSU is present, False if not + """ + # Get path for access port presence status + sysfs_filename = "sfp_modabs" if self.sfp_type == SFP_TYPE else "qsfp_modprs" + reg_path = "/".join([PORT_INFO_PATH, self.port_name, sysfs_filename]) + + content = self._api_helper.read_one_line_file(reg_path) + reg_value = int(content) + + return (reg_value == 0) + + 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 not self.get_reset_status() diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/thermal.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/thermal.py new file mode 100644 index 000000000000..755a168b06b8 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/thermal.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Thermal contains an implementation of SONiC Platform Base API and +# provides the thermal device status which are available in the platform +# +############################################################################# + +try: + from sonic_platform_base.thermal_base import ThermalBase + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +IPMI_GET_TEM="ipmitool raw 0x04 0x2D " +IPMI_GET_HIGH_THR="ipmitool raw 0x04 0x27 " +IPMI_SET_THR="ipmitool sensor thresh " +IPMI_SDR='ipmitool sdr | grep ' +DEFAULT_VAL = 'N/A' +psu_tem=[9,10,11,12] + +class Thermal(ThermalBase): + """Platform-specific Thermal class""" + + def __init__(self, thermal_index): + ThermalBase.__init__(self) + self.thermal_index = thermal_index + self._api_helper = APIHelper() + + ###### Thermal list defined ####### + # (NAME , val, set high threshold) + self.THERMAL_LIST = [ + ('Base_Temp_U5', '0x1', ' unc '), + ('Base_Temp_U7', '0x2', ' unc '), + ('CPU_Temp', '0x7', ' unc '), + ('Switch_Temp_U1', '0x3', ' unc '), + ('Switch_Temp_U18', '0x4', ' unc '), + ('Switch_Temp_U28', '0x5', ' unc '), + ('Switch_Temp_U29', '0x6', ' unc '), + ('Switch_U21_Temp', '0x56', ' unc '), + ('Switch_U33_Temp', '0x4c', ' unc '), + ('PSUL_Temp1', '0x0', ' unc '), + ('PSUL_Temp2', '0x0', ' unc '), + ('PSUR_Temp1', '0x0', ' unc '), + ('PSUR_Temp2', '0x0', ' unc ') + ] + + def get_temperature(self): + """ + Retrieves current temperature reading from thermal + Returns: + A float number of current temperature in Celsius up to nearest thousandth + of one degree Celsius, e.g. 30.125 + """ + if self.thermal_index in psu_tem: + get_tem=IPMI_SDR+self.THERMAL_LIST[self.thermal_index][0] + else: + get_tem=IPMI_GET_TEM+self.THERMAL_LIST[self.thermal_index][1] + status, raw_tem=self._api_helper.run_command(get_tem) + if status: + if self.thermal_index in psu_tem: + return float(int(raw_tem.split()[2], 10)) + return float(int(raw_tem.split()[0], 16)) + else: + return DEFAULT_VAL + + 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 + """ + no_thre=[2,7,8,9,10,11,12] + default_thre=80.0 + if self.thermal_index in no_thre: + return default_thre + + get_thre=IPMI_GET_HIGH_THR+self.THERMAL_LIST[self.thermal_index][1] + status, raw_thre=self._api_helper.run_command(get_thre) + if status: + return float(int(raw_thre.split()[4], 16)) + else: + return DEFAULT_VAL + + def set_high_threshold(self, temperature): + """ + Sets the high threshold temperature of thermal + 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 + """ + set_thre=IPMI_SET_THR+self.THERMAL_LIST[self.thermal_index][0]+\ + self.THERMAL_LIST[self.thermal_index][2]+str(temperature) + status, raw_thre=self._api_helper.run_command(set_thre) + if status: + return True + else: + return False + + + def get_low_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 + """ + return DEFAULT_VAL + + + def set_low_threshold(self, temperature): + """ + Sets the high threshold temperature of thermal + 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 + """ + return True + + def get_high_critical_threshold(self): + """ + Retrieves the high critical threshold temperature of thermal + Returns: + A float number, the high critical threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + no_thre=[7,8,9,10,11,12] + if self.thermal_index in no_thre: + return DEFAULT_VAL + + get_thre=IPMI_GET_HIGH_THR+self.THERMAL_LIST[self.thermal_index][1] + status, raw_thre=self._api_helper.run_command(get_thre) + if status: + return float(int(raw_thre.split()[5], 16)) + else: + return DEFAULT_VAL + + def get_low_critical_threshold(self): + """ + Retrieves the low critical threshold temperature of thermal + Returns: + A float number, the low critical threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + return DEFAULT_VAL + + ############################################################## + ###################### Device methods ######################## + ############################################################## + + def get_name(self): + """ + Retrieves the name of the thermal device + Returns: + string: The name of the thermal device + """ + name = self.THERMAL_LIST[self.thermal_index][0] + if self.thermal_index in psu_tem: + if 'PSUL' in name: + name = name.replace('PSUL', 'PSU1') + elif 'PSUR' in name: + name = name.replace('PSUR', 'PSU2') + return name + + 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 DEFAULT_VAL + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return DEFAULT_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 diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/watchdog.py b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/watchdog.py new file mode 100644 index 000000000000..07b2ef5cf676 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/sonic_platform/watchdog.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica Qeastone2 +# +# Watchdog contains an implementation of SONiC Platform Base API +# +############################################################################# +import subprocess + +try: + from sonic_platform_base.watchdog_base import WatchdogBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +I2C_WDT_BUS_ID=60 +I2C_WDT_DEV_ID=0x0d +I2C_WDT_RESET_SRC_REG=0x06 +I2C_WDT_CTRL_REG=0x81 +I2C_WDT_SET_TIMER_L_REG=0x82 +I2C_WDT_SET_TIMER_M_REG=0x83 +I2C_WDT_SET_TIMER_H_REG=0x84 +I2C_WDT_ARM_REG=0x85 +I2C_WDT_TIMER_L_REG=0x86 +I2C_WDT_TIMER_M_REG=0x87 +I2C_WDT_TIMER_H_REG=0x88 + +WDT_ENABLE=0x1 +WDT_DISABLE=0x0 +WDT_KEEPALIVE=0x1 +WDT_COMMON_ERROR=-1 +DEFAULT_TIMEOUT=180 + +class Watchdog(WatchdogBase): + + def __init__(self): + # Set default value + self.armed = True if self._active() else False + self.timeout = self._gettimeout() + #self._disable() + + def _i2cget_cmd(self, reg): + cmd = "i2cget -y -f {} {} {} b".format(I2C_WDT_BUS_ID, I2C_WDT_DEV_ID, + reg) + return cmd + + def _i2cset_cmd(self, reg, val): + cmd = "i2cset -y -f {} {} {} {}".format(I2C_WDT_BUS_ID, I2C_WDT_DEV_ID, + reg, val) + return cmd + + def _getstatusoutput(self, cmd): + try: + data = subprocess.check_output(cmd, shell=True, + universal_newlines=True, stderr=subprocess.STDOUT) + status = 0 + except subprocess.CalledProcessError as ex: + data = ex.output + status = ex.returncode + if data[-1:] == '\n': + data = data[:-1] + return status, data + + def _active(self): + """ + WDT is active or not + """ + status, data = self._getstatusoutput(self._i2cget_cmd(I2C_WDT_CTRL_REG)) + if status: + pass + return True if data == "0x01" else False + + def _enable(self): + """ + Turn on the watchdog timer + """ + status, data = self._getstatusoutput(self._i2cset_cmd(I2C_WDT_CTRL_REG, + WDT_ENABLE)) + if status: + pass + + def _disable(self): + """ + Turn off the watchdog timer + """ + status, data = self._getstatusoutput(self._i2cset_cmd(I2C_WDT_CTRL_REG, + WDT_DISABLE)) + if status: + pass + + def _keepalive(self): + """ + Keep alive watchdog timer + """ + self._settimeleft(0) + status, data = self._getstatusoutput(self._i2cset_cmd(I2C_WDT_ARM_REG, + WDT_KEEPALIVE)) + if status: + pass + + def _settimeout(self, seconds): + """ + Set watchdog timer timeout + @param seconds - timeout in seconds + @return is the actual set timeout + """ + ms = seconds * 1000 + ms_low_byte = ms & 0xff + ms_media_byte = (ms >> 8) & 0xff + ms_high_byte = (ms >> 16) & 0xff + status, data = self._getstatusoutput(self._i2cset_cmd(I2C_WDT_SET_TIMER_L_REG, + ms_low_byte)) + if status: + pass + status, data = self._getstatusoutput(self._i2cset_cmd(I2C_WDT_SET_TIMER_M_REG, + ms_media_byte)) + if status: + pass + status, data = self._getstatusoutput(self._i2cset_cmd(I2C_WDT_SET_TIMER_H_REG, + ms_high_byte)) + if status: + pass + return self._gettimeout() + + def _gettimeout(self): + """ + Get watchdog timeout + @return watchdog timeout + """ + data = [0, 0, 0] + status, data[0] = self._getstatusoutput(self._i2cget_cmd(I2C_WDT_SET_TIMER_L_REG)) + if status: + pass + status, data[1] = self._getstatusoutput(self._i2cget_cmd(I2C_WDT_SET_TIMER_M_REG)) + if status: + pass + status, data[2] = self._getstatusoutput(self._i2cget_cmd(I2C_WDT_SET_TIMER_H_REG)) + if status: + pass + seconds = int((int(data[2], 16) << 16 + | int(data[1], 16) << 8 + | int(data[0], 16)) / 1000) + + return seconds + + def _settimeleft(self, seconds): + """ + Set watchdog timer timeout + @param seconds - timeout in seconds + @return is the actual set timeout + """ + ms = seconds * 1000 + ms_low_byte = ms & 0xff + ms_media_byte = (ms >> 8) & 0xff + ms_high_byte = (ms >> 16) & 0xff + status, data = self._getstatusoutput(self._i2cset_cmd(I2C_WDT_TIMER_L_REG, + ms_low_byte)) + if status: + pass + status, data = self._getstatusoutput(self._i2cset_cmd(I2C_WDT_TIMER_M_REG, + ms_media_byte)) + if status: + pass + status, data = self._getstatusoutput(self._i2cset_cmd(I2C_WDT_TIMER_H_REG, + ms_high_byte)) + if status: + pass + + def _gettimeleft(self): + """ + Get time left before watchdog timer expires + @return time left in seconds + """ + data = [0, 0, 0] + status, data[0] = self._getstatusoutput(self._i2cget_cmd(I2C_WDT_TIMER_L_REG)) + if status: + pass + status, data[1] = self._getstatusoutput(self._i2cget_cmd(I2C_WDT_TIMER_M_REG)) + if status: + pass + status, data[2] = self._getstatusoutput(self._i2cget_cmd(I2C_WDT_TIMER_H_REG)) + if status: + pass + seconds = int((int(data[2], 16) << 16 + | int(data[1], 16) << 8 + | int(data[0], 16)) / 1000) + + return (self.timeout - seconds) if (self.timeout > seconds) else -1 + + ################################################################# + + 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._settimeout(seconds) + self._keepalive() + self._enable() + self.armed = True + ret = self.timeout + 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. + """ + + timeleft = WDT_COMMON_ERROR + + if self.armed: + try: + timeleft = self._gettimeleft() + except IOError: + pass + + return timeleft diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2/systemd/platform-modules-questone2.service b/platform/broadcom/sonic-platform-modules-cel/questone2/systemd/platform-modules-questone2.service new file mode 100644 index 000000000000..c6ad69112edb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2/systemd/platform-modules-questone2.service @@ -0,0 +1,13 @@ +[Unit] +Description=Celestica questone2 platform modules +After=local-fs.target +Before=pmon.service + +[Service] +Type=oneshot +ExecStart=-/etc/init.d/platform-modules-questone2 start +ExecStop=-/etc/init.d/platform-modules-questone2 stop +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/ps_mem.py b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/ps_mem.py new file mode 100755 index 000000000000..719b2d0d325d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/ps_mem.py @@ -0,0 +1,666 @@ +#!/usr/bin/python3 + +# Try to determine how much RAM is currently being used per program. +# Note per _program_, not per process. So for example this script +# will report RAM used by all httpd process together. In detail it reports: +# sum(private RAM for program processes) + sum(Shared RAM for program processes) +# The shared RAM is problematic to calculate, and this script automatically +# selects the most accurate method available for your kernel. + +# Licence: LGPLv2 +# Author: P@draigBrady.com +# Source: https://www.pixelbeat.org/scripts/ps_mem.py + +# V1.0 06 Jul 2005 Initial release +# V1.1 11 Aug 2006 root permission required for accuracy +# V1.2 08 Nov 2006 Add total to output +# Use KiB,MiB,... for units rather than K,M,... +# V1.3 22 Nov 2006 Ignore shared col from /proc/$pid/statm for +# 2.6 kernels up to and including 2.6.9. +# There it represented the total file backed extent +# V1.4 23 Nov 2006 Remove total from output as it's meaningless +# (the shared values overlap with other programs). +# Display the shared column. This extra info is +# useful, especially as it overlaps between programs. +# V1.5 26 Mar 2007 Remove redundant recursion from human() +# V1.6 05 Jun 2007 Also report number of processes with a given name. +# Patch from riccardo.murri@gmail.com +# V1.7 20 Sep 2007 Use PSS from /proc/$pid/smaps if available, which +# fixes some over-estimation and allows totalling. +# Enumerate the PIDs directly rather than using ps, +# which fixes the possible race between reading +# RSS with ps, and shared memory with this program. +# Also we can show non truncated command names. +# V1.8 28 Sep 2007 More accurate matching for stats in /proc/$pid/smaps +# as otherwise could match libraries causing a crash. +# Patch from patrice.bouchand.fedora@gmail.com +# V1.9 20 Feb 2008 Fix invalid values reported when PSS is available. +# Reported by Andrey Borzenkov +# V3.14 28 May 2022 +# https://github.com/pixelb/ps_mem/commits/master/ps_mem.py + +# Notes: +# +# All interpreted programs where the interpreter is started +# by the shell or with env, will be merged to the interpreter +# (as that's what's given to exec). For e.g. all python programs +# starting with "#!/usr/bin/env python" will be grouped under python. +# You can change this by using the full command line but that will +# have the undesirable affect of splitting up programs started with +# differing parameters (for e.g. mingetty tty[1-6]). +# +# For 2.6 kernels up to and including 2.6.13 and later 2.4 redhat kernels +# (rmap vm without smaps) it can not be accurately determined how many pages +# are shared between processes in general or within a program in our case: +# http://lkml.org/lkml/2005/7/6/250 +# A warning is printed if overestimation is possible. +# In addition for 2.6 kernels up to 2.6.9 inclusive, the shared +# value in /proc/$pid/statm is the total file-backed extent of a process. +# We ignore that, introducing more overestimation, again printing a warning. +# Since kernel 2.6.23-rc8-mm1 PSS is available in smaps, which allows +# us to calculate a more accurate value for the total RAM used by programs. +# +# Programs that use CLONE_VM without CLONE_THREAD are discounted by assuming +# they're the only programs that have the same /proc/$PID/smaps file for +# each instance. This will fail if there are multiple real instances of a +# program that then use CLONE_VM without CLONE_THREAD, or if a clone changes +# its memory map while we're checksumming each /proc/$PID/smaps. +# +# I don't take account of memory allocated for a program +# by other programs. For e.g. memory used in the X server for +# a program could be determined, but is not. +# +# FreeBSD is supported if linprocfs is mounted at /compat/linux/proc/ +# FreeBSD 8.0 supports up to a level of Linux 2.6.16 + +import argparse +import errno +import os +import sys +import time +import io + +# The following exits cleanly on Ctrl-C or EPIPE +# while treating other exceptions as before. +def std_exceptions(etype, value, tb): + sys.excepthook = sys.__excepthook__ + if issubclass(etype, KeyboardInterrupt): + pass + elif issubclass(etype, IOError) and value.errno == errno.EPIPE: + pass + else: + sys.__excepthook__(etype, value, tb) +sys.excepthook = std_exceptions + +# +# Define some global variables +# + +PAGESIZE = os.sysconf("SC_PAGE_SIZE") / 1024 #KiB +our_pid = os.getpid() + +have_pss = 0 +have_swap_pss = 0 + +class Unbuffered(io.TextIOBase): + def __init__(self, stream): + super(Unbuffered, self).__init__() + self.stream = stream + def write(self, data): + self.stream.write(data) + self.stream.flush() + def close(self): + self.stream.close() + +class Proc: + def __init__(self): + uname = os.uname() + if uname[0] == "FreeBSD": + self.proc = '/compat/linux/proc' + else: + self.proc = '/proc' + + def path(self, *args): + return os.path.join(self.proc, *(str(a) for a in args)) + + def open(self, *args): + try: + if sys.version_info < (3,): + return open(self.path(*args)) + else: + return open(self.path(*args), errors='ignore') + except (IOError, OSError): + if type(args[0]) is not int: + raise + val = sys.exc_info()[1] + if (val.errno == errno.ENOENT or # kernel thread or process gone + val.errno == errno.EPERM or + val.errno == errno.EACCES): + raise LookupError + raise + +proc = Proc() + + +# +# Functions +# + +def parse_options(): + help_msg = 'Show program core memory usage.' + parser = argparse.ArgumentParser(prog='ps_mem', description=help_msg) + parser.add_argument('--version', action='version', version='3.14') + parser.add_argument( + '-s', '--split-args', + action='store_true', + help='Show and separate by, all command line arguments', + ) + parser.add_argument( + '-t', '--total', + dest='only_total', + action='store_true', + help='Show only the total value', + ) + parser.add_argument( + '-d', '--discriminate-by-pid', + action='store_true', + help='Show by process rather than by program', + ) + parser.add_argument( + '-S', '--swap', + dest='show_swap', + action='store_true', + help='Show swap information', + ) + parser.add_argument( + '-p', + dest='pids', + metavar='[,pid2,...pidN]', + help='Only show memory usage PIDs in the specified list', + ) + parser.add_argument( + '-w', + dest='watch', + metavar='', + type=int, + help='Measure and show process memory every N seconds', + ) + args = parser.parse_args() + + args.pids_to_show = [] + if args.pids: + try: + args.pids_to_show = [int(x) for x in args.pids.split(',')] + except ValueError: + parser.error('Invalid PID(s): %s' % args.pids) + + if args.watch is not None: + if args.watch <= 0: + parser.error('Seconds must be positive! (%s)' % args.watch) + + return ( + args.split_args, + args.pids_to_show, + args.watch, + args.only_total, + args.discriminate_by_pid, + args.show_swap, + ) + + +# (major,minor,release) +def kernel_ver(): + kv = proc.open('sys/kernel/osrelease').readline().split(".")[:3] + last = len(kv) + if last == 2: + kv.append('0') + last -= 1 + while last > 0: + for char in "-_": + kv[last] = kv[last].split(char)[0] + try: + int(kv[last]) + except: + kv[last] = 0 + last -= 1 + return (int(kv[0]), int(kv[1]), int(kv[2])) + + +#return Private,Shared,Swap(Pss),unique_id +#Note shared is always a subset of rss (trs is not always) +def getMemStats(pid): + global have_pss + global have_swap_pss + mem_id = pid #unique + Private_lines = [] + Shared_lines = [] + Private_huge_lines = [] + Shared_huge_lines = [] + Pss_lines = [] + Rss = (int(proc.open(pid, 'statm').readline().split()[1]) + * PAGESIZE) + Swap_lines = [] + Swap_pss_lines = [] + + Swap = 0 + + if os.path.exists(proc.path(pid, 'smaps')): # stat + smaps = 'smaps' + if os.path.exists(proc.path(pid, 'smaps_rollup')): + smaps = 'smaps_rollup' # faster to process + lines = proc.open(pid, smaps).readlines() # open + # Note we checksum smaps as maps is usually but + # not always different for separate processes. + mem_id = hash(''.join(lines)) + for line in lines: + # {Private,Shared}_Hugetlb is not included in Pss (why?) + # so we need to account for separately. + if line.startswith("Private_Hugetlb:"): + Private_huge_lines.append(line) + elif line.startswith("Shared_Hugetlb:"): + Shared_huge_lines.append(line) + elif line.startswith("Shared"): + Shared_lines.append(line) + elif line.startswith("Private"): + Private_lines.append(line) + elif line.startswith("Pss:"): + have_pss = 1 + Pss_lines.append(line) + elif line.startswith("Swap:"): + Swap_lines.append(line) + elif line.startswith("SwapPss:"): + have_swap_pss = 1 + Swap_pss_lines.append(line) + Shared = sum([int(line.split()[1]) for line in Shared_lines]) + Private = sum([int(line.split()[1]) for line in Private_lines]) + Shared_huge = sum([int(line.split()[1]) for line in Shared_huge_lines]) + Private_huge = sum([int(line.split()[1]) for line in Private_huge_lines]) + #Note Shared + Private = Rss above + #The Rss in smaps includes video card mem etc. + if have_pss: + pss_adjust = 0.5 # add 0.5KiB as this avg error due to truncation + Pss = sum([float(line.split()[1])+pss_adjust for line in Pss_lines]) + Shared = Pss - Private + Private += Private_huge # Add after as PSS doesn't a/c for huge pages + if have_swap_pss: + # The kernel supports SwapPss, that shows proportional swap share. + # Note that Swap - SwapPss is not Private Swap. + Swap = sum([int(line.split()[1]) for line in Swap_pss_lines]) + else: + # Note that Swap = Private swap + Shared swap. + Swap = sum([int(line.split()[1]) for line in Swap_lines]) + elif (2,6,1) <= kernel_ver() <= (2,6,9): + Shared = 0 #lots of overestimation, but what can we do? + Shared_huge = 0 + Private = Rss + else: + Shared = int(proc.open(pid, 'statm').readline().split()[2]) + Shared *= PAGESIZE + Shared_huge = 0 + Private = Rss - Shared + return (Private, Shared, Shared_huge, Swap, mem_id) + + +def getCmdName(pid, split_args, discriminate_by_pid, exe_only=False): + cmdline = proc.open(pid, 'cmdline').read().split("\0") + while cmdline[-1] == '' and len(cmdline) > 1: + cmdline = cmdline[:-1] + + path = proc.path(pid, 'exe') + try: + path = os.readlink(path) + # Some symlink targets were seen to contain NULs on RHEL 5 at least + # https://github.com/pixelb/scripts/pull/10, so take string up to NUL + path = path.split('\0')[0] + except OSError: + val = sys.exc_info()[1] + if (val.errno == errno.ENOENT or # either kernel thread or process gone + val.errno == errno.EPERM or + val.errno == errno.EACCES): + raise LookupError + raise + + if split_args: + return ' '.join(cmdline).replace('\n', ' ') + if path.endswith(" (deleted)"): + path = path[:-10] + if os.path.exists(path): + path += " [updated]" + else: + #The path could be have prelink stuff so try cmdline + #which might have the full path present. This helped for: + #/usr/libexec/notification-area-applet.#prelink#.fX7LCT (deleted) + if os.path.exists(cmdline[0]): + path = cmdline[0] + " [updated]" + else: + path += " [deleted]" + exe = os.path.basename(path) + if exe_only: return exe + + proc_status = proc.open(pid, 'status').readlines() + cmd = proc_status[0][6:-1] + if exe.startswith(cmd): + cmd = exe #show non truncated version + #Note because we show the non truncated name + #one can have separated programs as follows: + #584.0 KiB + 1.0 MiB = 1.6 MiB mozilla-thunder (exe -> bash) + # 56.0 MiB + 22.2 MiB = 78.2 MiB mozilla-thunderbird-bin + else: + #Lookup the parent's exe and use that if matching + #which will merge "Web Content" with "firefox" for example + ppid = 0 + for l in range(10): + ps_line = proc_status[l] + if ps_line.startswith('PPid:'): + ppid = int(ps_line[6:-1]) + break + if ppid: + try: + p_exe = getCmdName(ppid, False, False, exe_only=True) + except LookupError: + pass + else: + if exe == p_exe: + cmd = exe + if sys.version_info >= (3,): + cmd = cmd.encode(errors='replace').decode() + if discriminate_by_pid: + cmd = '%s [%d]' % (cmd, pid) + return cmd + + +#The following matches "du -h" output +#see also human.py +def human(num, power="Ki", units=None): + if units is None: + powers = ["Ki", "Mi", "Gi", "Ti"] + while num >= 1000: #4 digits + num /= 1024.0 + power = powers[powers.index(power)+1] + return "%.1f %sB" % (num, power) + else: + return "%.f" % ((num * 1024) / units) + + +def cmd_with_count(cmd, count): + if count > 1: + return "%s (%u)" % (cmd, count) + else: + return cmd + +#Warn of possible inaccuracies +#RAM: +#2 = accurate & can total +#1 = accurate only considering each process in isolation +#0 = some shared mem not reported +#-1= all shared mem not reported +#SWAP: +#2 = accurate & can total +#1 = accurate only considering each process in isolation +#-1= not available +def val_accuracy(show_swap): + """http://wiki.apache.org/spamassassin/TopSharedMemoryBug""" + kv = kernel_ver() + pid = os.getpid() + swap_accuracy = -1 + if kv[:2] == (2,4): + if proc.open('meminfo').read().find("Inact_") == -1: + return 1, swap_accuracy + return 0, swap_accuracy + elif kv[:2] == (2,6): + if os.path.exists(proc.path(pid, 'smaps')): + swap_accuracy = 1 + if proc.open(pid, 'smaps').read().find("Pss:")!=-1: + return 2, swap_accuracy + else: + return 1, swap_accuracy + if (2,6,1) <= kv <= (2,6,9): + return -1, swap_accuracy + return 0, swap_accuracy + elif kv[0] > 2 and os.path.exists(proc.path(pid, 'smaps')): + swap_accuracy = 1 + if show_swap and proc.open(pid, 'smaps').read().find("SwapPss:")!=-1: + swap_accuracy = 2 + return 2, swap_accuracy + else: + return 1, swap_accuracy + +def show_val_accuracy( ram_inacc, swap_inacc, only_total, show_swap ): + level = ("Warning","Error")[only_total] + + # Only show significant warnings + if not show_swap: + swap_inacc = 2 + elif only_total: + ram_inacc = 2 + + if ram_inacc == -1: + sys.stderr.write( + "%s: Shared memory is not reported by this system.\n" % level + ) + sys.stderr.write( + "Values reported will be too large, and totals are not reported\n" + ) + elif ram_inacc == 0: + sys.stderr.write( + "%s: Shared memory is not reported accurately by this system.\n" % level + ) + sys.stderr.write( + "Values reported could be too large, and totals are not reported\n" + ) + elif ram_inacc == 1: + sys.stderr.write( + "%s: Shared memory is slightly over-estimated by this system\n" + "for each program, so totals are not reported.\n" % level + ) + + if swap_inacc == -1: + sys.stderr.write( + "%s: Swap is not reported by this system.\n" % level + ) + elif swap_inacc == 1: + sys.stderr.write( + "%s: Swap is over-estimated by this system for each program,\n" + "so totals are not reported.\n" % level + ) + + sys.stderr.close() + if only_total: + if show_swap: + accuracy = swap_inacc + else: + accuracy = ram_inacc + if accuracy != 2: + sys.exit(1) + + +def get_memory_usage(pids_to_show, split_args, discriminate_by_pid, + include_self=False, only_self=False): + cmds = {} + shareds = {} + shared_huges = {} + mem_ids = {} + count = {} + swaps = {} + for pid in os.listdir(proc.path('')): + if not pid.isdigit(): + continue + pid = int(pid) + + # Some filters + if only_self and pid != our_pid: + continue + if pid == our_pid and not include_self: + continue + if pids_to_show and pid not in pids_to_show: + continue + + try: + cmd = getCmdName(pid, split_args, discriminate_by_pid) + except LookupError: + #operation not permitted + #kernel threads don't have exe links or + #process gone + continue + + try: + private, shared, shared_huge, swap, mem_id = getMemStats(pid) + except RuntimeError: + continue #process gone + if shareds.get(cmd): + if have_pss: #add shared portion of PSS together + shareds[cmd] += shared + elif shareds[cmd] < shared: #just take largest shared val + shareds[cmd] = shared + else: + shareds[cmd] = shared + if shared_huges.get(cmd): + if shared_huges[cmd] < shared_huge: #just take largest shared_huge + shared_huges[cmd] = shared_huge + else: + shared_huges[cmd] = shared_huge + cmds[cmd] = cmds.setdefault(cmd, 0) + private + if cmd in count: + count[cmd] += 1 + else: + count[cmd] = 1 + mem_ids.setdefault(cmd, {}).update({mem_id: None}) + + # Swap (overcounting for now...) + swaps[cmd] = swaps.setdefault(cmd, 0) + swap + + # Total swaped mem for each program + total_swap = 0 + + # Add shared mem for each program + total = 0 + + for cmd in cmds: + cmd_count = count[cmd] + if len(mem_ids[cmd]) == 1 and cmd_count > 1: + # Assume this program is using CLONE_VM without CLONE_THREAD + # so only account for one of the processes + cmds[cmd] /= cmd_count + if have_pss: + shareds[cmd] /= cmd_count + # overestimation possible if shared_huges shared across commands + shareds[cmd] += shared_huges[cmd] + cmds[cmd] = cmds[cmd] + shareds[cmd] + total += cmds[cmd] # valid if PSS available + total_swap += swaps[cmd] + + sorted_cmds = sorted(cmds.items(), key=lambda x:x[1]) + sorted_cmds = [x for x in sorted_cmds if x[1]] + + return sorted_cmds, shareds, count, total, swaps, total_swap + +def print_header(show_swap, discriminate_by_pid): + output_string = " Private + Shared = RAM used" + if show_swap: + output_string += " Swap used" + output_string += "\tProgram" + if discriminate_by_pid: + output_string += "[pid]" + output_string += "\n\n" + sys.stdout.write(output_string) + + +def print_memory_usage(sorted_cmds, shareds, count, total, swaps, total_swap, + show_swap): + for cmd in sorted_cmds: + + output_string = "%9s + %9s = %9s" + output_data = (human(cmd[1]-shareds[cmd[0]]), + human(shareds[cmd[0]]), human(cmd[1])) + if show_swap: + output_string += " %9s" + output_data += (human(swaps[cmd[0]]),) + output_string += "\t%s\n" + output_data += (cmd_with_count(cmd[0], count[cmd[0]]),) + + sys.stdout.write(output_string % output_data) + + # Only show totals if appropriate + if have_swap_pss and show_swap: # kernel will have_pss + sys.stdout.write("%s\n%s%9s%s%9s\n%s\n" % + ("-" * 45, " " * 24, human(total), " " * 3, + human(total_swap), "=" * 45)) + elif have_pss: + sys.stdout.write("%s\n%s%9s\n%s\n" % + ("-" * 33, " " * 24, human(total), "=" * 33)) + + +def verify_environment(pids_to_show): + if os.geteuid() != 0 and not pids_to_show: + sys.stderr.write("Sorry, root permission required, or specify pids with -p\n") + sys.stderr.close() + sys.exit(1) + + try: + kernel_ver() + except (IOError, OSError): + val = sys.exc_info()[1] + if val.errno == errno.ENOENT: + sys.stderr.write( + "Couldn't access " + proc.path('') + "\n" + "Only GNU/Linux and FreeBSD (with linprocfs) are supported\n") + sys.exit(2) + else: + raise + +def main(): + # Force the stdout and stderr streams to be unbuffered + sys.stdout = Unbuffered(sys.stdout) + sys.stderr = Unbuffered(sys.stderr) + + split_args, pids_to_show, watch, only_total, discriminate_by_pid, \ + show_swap = parse_options() + + verify_environment(pids_to_show) + + if not only_total: + print_header(show_swap, discriminate_by_pid) + + if watch is not None: + try: + sorted_cmds = True + while sorted_cmds: + sorted_cmds, shareds, count, total, swaps, total_swap = \ + get_memory_usage(pids_to_show, split_args, + discriminate_by_pid) + if only_total and show_swap and have_swap_pss: + sys.stdout.write(human(total_swap, units=1)+'\n') + elif only_total and not show_swap and have_pss: + sys.stdout.write(human(total, units=1)+'\n') + elif not only_total: + print_memory_usage(sorted_cmds, shareds, count, total, + swaps, total_swap, show_swap) + + sys.stdout.flush() + time.sleep(watch) + else: + sys.stdout.write('Process does not exist anymore.\n') + except KeyboardInterrupt: + pass + else: + # This is the default behavior + sorted_cmds, shareds, count, total, swaps, total_swap = \ + get_memory_usage(pids_to_show, split_args, + discriminate_by_pid) + if only_total and show_swap and have_swap_pss: + sys.stdout.write(human(total_swap, units=1)+'\n') + elif only_total and not show_swap and have_pss: + sys.stdout.write(human(total, units=1)+'\n') + elif not only_total: + print_memory_usage(sorted_cmds, shareds, count, total, swaps, + total_swap, show_swap) + + # We must close explicitly, so that any EPIPE exception + # is handled by our excepthook, rather than the default + # one which is reenabled after this script finishes. + sys.stdout.close() + + ram_accuracy, swap_accuracy = val_accuracy( show_swap ) + show_val_accuracy( ram_accuracy, swap_accuracy, only_total, show_swap ) + +if __name__ == '__main__': main()