diff --git a/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/port_config.ini b/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/port_config.ini new file mode 100644 index 0000000000..ed6bcff233 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/port_config.ini @@ -0,0 +1,33 @@ +# name lanes alias fec index speed +Ethernet0 1,2,3,4 QSFP1 rs 1 100000 +Ethernet4 5,6,7,8 QSFP2 rs 2 100000 +Ethernet8 9,10,11,12 QSFP3 rs 3 100000 +Ethernet12 13,14,15,16 QSFP4 rs 4 100000 +Ethernet16 17,18,19,20 QSFP5 rs 5 100000 +Ethernet20 21,22,23,24 QSFP6 rs 6 100000 +Ethernet24 25,26,27,28 QSFP7 rs 7 100000 +Ethernet28 29,30,31,32 QSFP8 rs 8 100000 +Ethernet32 33,34,35,36 QSFP9 rs 9 100000 +Ethernet36 37,38,39,40 QSFP10 rs 10 100000 +Ethernet40 41,42,43,44 QSFP11 rs 11 100000 +Ethernet44 45,46,47,48 QSFP12 rs 12 100000 +Ethernet48 49,50,51,52 QSFP13 rs 13 100000 +Ethernet52 53,54,55,56 QSFP14 rs 14 100000 +Ethernet56 57,58,59,60 QSFP15 rs 15 100000 +Ethernet60 61,62,63,64 QSFP16 rs 16 100000 +Ethernet64 65,66,67,68 QSFP17 rs 17 100000 +Ethernet68 69,70,71,72 QSFP18 rs 18 100000 +Ethernet72 73,74,75,76 QSFP19 rs 19 100000 +Ethernet76 77,78,79,80 QSFP20 rs 20 100000 +Ethernet80 81,82,83,84 QSFP21 rs 21 100000 +Ethernet84 85,86,87,88 QSFP22 rs 22 100000 +Ethernet88 89,90,91,92 QSFP23 rs 23 100000 +Ethernet92 93,94,95,96 QSFP24 rs 24 100000 +Ethernet96 97,98,99,100 QSFP25 rs 25 100000 +Ethernet100 101,102,103,104 QSFP26 rs 26 100000 +Ethernet104 105,106,107,108 QSFP27 rs 27 100000 +Ethernet108 109,110,111,112 QSFP28 rs 28 100000 +Ethernet112 113,114,115,116 QSFP29 rs 29 100000 +Ethernet116 117,118,119,120 QSFP30 rs 30 100000 +Ethernet120 121,122,123,124 QSFP31 rs 31 100000 +Ethernet124 125,126,127,128 QSFP32 rs 32 100000 \ No newline at end of file diff --git a/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/sai.profile b/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/sai.profile new file mode 100644 index 0000000000..ea150bf237 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/sai.profile @@ -0,0 +1 @@ +SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/td3-seastone_2-32x100G.config.bcm diff --git a/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/td3-seastone_2-32x100G.config.bcm b/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/td3-seastone_2-32x100G.config.bcm new file mode 100644 index 0000000000..2c23d0da2f --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/Seastone_2/td3-seastone_2-32x100G.config.bcm @@ -0,0 +1,597 @@ +help_cli_enable=1 +ifp_inports_support_enable=1 +ipv6_lpm_128b_enable=0x1 +l2_mem_entries=32768 +l2xmsg_mode=1 +l3_max_ecmp_mode=1 +l3_mem_entries=16384 +lpm_scaling_enable=1 +max_vp_lags=0 +mem_cache_enable=0 +memlist_enable=1 +miim_intr_enable=0 +module_64ports=1 +oversubscribe_mode=1 +parity_enable=0 +serdes_lane_config_dfe=on +#serdes_fec_enable=1 +serdes_if_type_ce=14 +pbmp_gport_stack.0=0x0000000000000000000000000000000000000000000000000000000000000000 +pbmp_xport_xe=0x88888888888888882222222222222222 + +ptp_ts_pll_fref=50000000 +ptp_bs_fref_0=50000000 +ptp_bs_fref_1=50000000 + +portmap_1.0=1:100 +portmap_5.0=5:100 +portmap_9.0=9:100 +portmap_13.0=13:100 +portmap_17.0=17:100 +portmap_21.0=21:100 +portmap_25.0=25:100 +portmap_29.0=29:100 +portmap_33.0=33:100 +portmap_37.0=37:100 +portmap_41.0=41:100 +portmap_45.0=45:100 +portmap_49.0=49:100 +portmap_53.0=53:100 +portmap_57.0=57:100 +portmap_61.0=61:100 +portmap_67.0=65:100 +portmap_71.0=69:100 +portmap_75.0=73:100 +portmap_79.0=77:100 +portmap_83.0=81:100 +portmap_87.0=85:100 +portmap_91.0=89:100 +portmap_95.0=93:100 +portmap_99.0=97:100 +portmap_103.0=101:100 +portmap_107.0=105:100 +portmap_111.0=109:100 +portmap_115.0=113:100 +portmap_119.0=117:100 +portmap_123.0=121:100 +portmap_127.0=125:100 +#portmap_66.0=129:10:m +#portmap_130.0=128:10:m + +#wc0 lane swap +phy_chain_tx_lane_map_physical{1.0}=0x0132 +phy_chain_rx_lane_map_physical{1.0}=0x3210 + +#wc1 lane swap +phy_chain_tx_lane_map_physical{5.0}=0x2301 +phy_chain_rx_lane_map_physical{5.0}=0x2031 + +#wc2 lane swap +phy_chain_tx_lane_map_physical{9.0}=0x0132 +phy_chain_rx_lane_map_physical{9.0}=0x3210 + +#wc3 lane swap +phy_chain_tx_lane_map_physical{13.0}=0x3201 +phy_chain_rx_lane_map_physical{13.0}=0x2031 + +#wc4 lane swap +phy_chain_tx_lane_map_physical{17.0}=0x0123 +phy_chain_rx_lane_map_physical{17.0}=0x3210 + +#wc5 lane swap +phy_chain_tx_lane_map_physical{21.0}=0x2301 +phy_chain_rx_lane_map_physical{21.0}=0x2031 + +#wc6 lane swap +phy_chain_tx_lane_map_physical{25.0}=0x0123 +phy_chain_rx_lane_map_physical{25.0}=0x3210 + +#wc7 lane swap +phy_chain_tx_lane_map_physical{29.0}=0x3201 +phy_chain_rx_lane_map_physical{29.0}=0x2031 + +#wc8 lane swap +phy_chain_tx_lane_map_physical{33.0}=0x0213 +phy_chain_rx_lane_map_physical{33.0}=0x1302 + +#wc9 lane swap +phy_chain_tx_lane_map_physical{37.0}=0x1302 +phy_chain_rx_lane_map_physical{37.0}=0x2031 + +#wc10 lane swap +phy_chain_tx_lane_map_physical{41.0}=0x0231 +phy_chain_rx_lane_map_physical{41.0}=0x3120 + +#wc11 lane swap +phy_chain_tx_lane_map_physical{45.0}=0x1302 +phy_chain_rx_lane_map_physical{45.0}=0x2031 + +#wc12 lane swap +phy_chain_tx_lane_map_physical{49.0}=0x2103 +phy_chain_rx_lane_map_physical{49.0}=0x3120 + +#wc13 lane swap +phy_chain_tx_lane_map_physical{53.0}=0x2301 +phy_chain_rx_lane_map_physical{53.0}=0x2031 + +#wc14 lane swap +phy_chain_tx_lane_map_physical{57.0}=0x0123 +phy_chain_rx_lane_map_physical{57.0}=0x2301 + +#wc15 lane swap +phy_chain_tx_lane_map_physical{61.0}=0x3210 +phy_chain_rx_lane_map_physical{61.0}=0x1032 + +#wc16 lane swap +phy_chain_tx_lane_map_physical{65.0}=0x3210 +phy_chain_rx_lane_map_physical{65.0}=0x1023 + +#wc17 lane swap +phy_chain_tx_lane_map_physical{69.0}=0x0123 +phy_chain_rx_lane_map_physical{69.0}=0x1302 + +#wc18 lane swap +phy_chain_tx_lane_map_physical{73.0}=0x2301 +phy_chain_rx_lane_map_physical{73.0}=0x1032 + +#wc19 lane swap +phy_chain_tx_lane_map_physical{77.0}=0x2013 +phy_chain_rx_lane_map_physical{77.0}=0x3120 + +#wc20 lane swap +phy_chain_tx_lane_map_physical{81.0}=0x1302 +phy_chain_rx_lane_map_physical{81.0}=0x2031 + +#wc21 lane swap +phy_chain_tx_lane_map_physical{85.0}=0x0123 +phy_chain_rx_lane_map_physical{85.0}=0x2130 + +#wc22 lane swap +phy_chain_tx_lane_map_physical{89.0}=0x2301 +phy_chain_rx_lane_map_physical{89.0}=0x2031 + +#wc23 lane swap +phy_chain_tx_lane_map_physical{93.0}=0x0312 +phy_chain_rx_lane_map_physical{93.0}=0x2310 + +#wc24 lane swap +phy_chain_tx_lane_map_physical{97.0}=0x2301 +phy_chain_rx_lane_map_physical{97.0}=0x1032 + +#wc25 lane swap +phy_chain_tx_lane_map_physical{101.0}=0x0123 +phy_chain_rx_lane_map_physical{101.0}=0x3210 + +#wc26 lane swap +phy_chain_tx_lane_map_physical{105.0}=0x2301 +phy_chain_rx_lane_map_physical{105.0}=0x1032 + +#wc27 lane swap +phy_chain_tx_lane_map_physical{109.0}=0x0123 +phy_chain_rx_lane_map_physical{109.0}=0x3210 + +#wc28 lane swap +phy_chain_tx_lane_map_physical{113.0}=0x2301 +phy_chain_rx_lane_map_physical{113.0}=0x2031 + +#wc29 lane swap +phy_chain_tx_lane_map_physical{117.0}=0x0123 +phy_chain_rx_lane_map_physical{117.0}=0x3210 + +#wc30 lane swap +phy_chain_tx_lane_map_physical{121.0}=0x2301 +phy_chain_rx_lane_map_physical{121.0}=0x1032 + +#wc31 lane swap +phy_chain_tx_lane_map_physical{125.0}=0x0123 +phy_chain_rx_lane_map_physical{125.0}=0x3210 + +#MC lane swap +phy_chain_tx_lane_map_physical{129.0}=0x3210 +phy_chain_rx_lane_map_physical{129.0}=0x3210 + + +#wc0 P/N flip +phy_chain_tx_polarity_flip_physical{1.0}=0x0 +phy_chain_rx_polarity_flip_physical{1.0}=0x0 +phy_chain_tx_polarity_flip_physical{2.0}=0x0 +phy_chain_rx_polarity_flip_physical{2.0}=0x1 +phy_chain_tx_polarity_flip_physical{3.0}=0x0 +phy_chain_rx_polarity_flip_physical{3.0}=0x0 +phy_chain_tx_polarity_flip_physical{4.0}=0x1 +phy_chain_rx_polarity_flip_physical{4.0}=0x1 + +#wc1 P/N flip +phy_chain_tx_polarity_flip_physical{5.0}=0x0 +phy_chain_rx_polarity_flip_physical{5.0}=0x0 +phy_chain_tx_polarity_flip_physical{6.0}=0x1 +phy_chain_rx_polarity_flip_physical{6.0}=0x1 +phy_chain_tx_polarity_flip_physical{7.0}=0x0 +phy_chain_rx_polarity_flip_physical{7.0}=0x1 +phy_chain_tx_polarity_flip_physical{8.0}=0x1 +phy_chain_rx_polarity_flip_physical{8.0}=0x1 + +#wc2 P/N flip +phy_chain_tx_polarity_flip_physical{9.0}=0x0 +phy_chain_rx_polarity_flip_physical{9.0}=0x0 +phy_chain_tx_polarity_flip_physical{10.0}=0x0 +phy_chain_rx_polarity_flip_physical{10.0}=0x1 +phy_chain_tx_polarity_flip_physical{11.0}=0x0 +phy_chain_rx_polarity_flip_physical{11.0}=0x0 +phy_chain_tx_polarity_flip_physical{12.0}=0x1 +phy_chain_rx_polarity_flip_physical{12.0}=0x1 + +#wc3 P/N flip +phy_chain_tx_polarity_flip_physical{13.0}=0x0 +phy_chain_rx_polarity_flip_physical{13.0}=0x0 +phy_chain_tx_polarity_flip_physical{14.0}=0x1 +phy_chain_rx_polarity_flip_physical{14.0}=0x1 +phy_chain_tx_polarity_flip_physical{15.0}=0x0 +phy_chain_rx_polarity_flip_physical{15.0}=0x1 +phy_chain_tx_polarity_flip_physical{16.0}=0x0 +phy_chain_rx_polarity_flip_physical{16.0}=0x1 + +#wc4 P/N flip +phy_chain_tx_polarity_flip_physical{17.0}=0x0 +phy_chain_rx_polarity_flip_physical{17.0}=0x0 +phy_chain_tx_polarity_flip_physical{18.0}=0x1 +phy_chain_rx_polarity_flip_physical{18.0}=0x1 +phy_chain_tx_polarity_flip_physical{19.0}=0x0 +phy_chain_rx_polarity_flip_physical{19.0}=0x0 +phy_chain_tx_polarity_flip_physical{20.0}=0x1 +phy_chain_rx_polarity_flip_physical{20.0}=0x1 + +#wc5 P/N flip +phy_chain_tx_polarity_flip_physical{21.0}=0x0 +phy_chain_rx_polarity_flip_physical{21.0}=0x0 +phy_chain_tx_polarity_flip_physical{22.0}=0x1 +phy_chain_rx_polarity_flip_physical{22.0}=0x1 +phy_chain_tx_polarity_flip_physical{23.0}=0x0 +phy_chain_rx_polarity_flip_physical{23.0}=0x1 +phy_chain_tx_polarity_flip_physical{24.0}=0x1 +phy_chain_rx_polarity_flip_physical{24.0}=0x1 + +#wc6 P/N flip +phy_chain_tx_polarity_flip_physical{25.0}=0x0 +phy_chain_rx_polarity_flip_physical{25.0}=0x1 +phy_chain_tx_polarity_flip_physical{26.0}=0x1 +phy_chain_rx_polarity_flip_physical{26.0}=0x0 +phy_chain_tx_polarity_flip_physical{27.0}=0x0 +phy_chain_rx_polarity_flip_physical{27.0}=0x1 +phy_chain_tx_polarity_flip_physical{28.0}=0x1 +phy_chain_rx_polarity_flip_physical{28.0}=0x0 + +#wc7 P/N flip +phy_chain_tx_polarity_flip_physical{29.0}=0x1 +phy_chain_rx_polarity_flip_physical{29.0}=0x1 +phy_chain_tx_polarity_flip_physical{30.0}=0x1 +phy_chain_rx_polarity_flip_physical{30.0}=0x0 +phy_chain_tx_polarity_flip_physical{31.0}=0x0 +phy_chain_rx_polarity_flip_physical{31.0}=0x0 +phy_chain_tx_polarity_flip_physical{32.0}=0x0 +phy_chain_rx_polarity_flip_physical{32.0}=0x0 + +#wc8 P/N flip +phy_chain_tx_polarity_flip_physical{33.0}=0x1 +phy_chain_rx_polarity_flip_physical{33.0}=0x1 +phy_chain_tx_polarity_flip_physical{34.0}=0x0 +phy_chain_rx_polarity_flip_physical{34.0}=0x0 +phy_chain_tx_polarity_flip_physical{35.0}=0x0 +phy_chain_rx_polarity_flip_physical{35.0}=0x0 +phy_chain_tx_polarity_flip_physical{36.0}=0x1 +phy_chain_rx_polarity_flip_physical{36.0}=0x0 + +#wc9 P/N flip +phy_chain_tx_polarity_flip_physical{37.0}=0x1 +phy_chain_rx_polarity_flip_physical{37.0}=0x1 +phy_chain_tx_polarity_flip_physical{38.0}=0x1 +phy_chain_rx_polarity_flip_physical{38.0}=0x0 +phy_chain_tx_polarity_flip_physical{39.0}=0x1 +phy_chain_rx_polarity_flip_physical{39.0}=0x0 +phy_chain_tx_polarity_flip_physical{40.0}=0x0 +phy_chain_rx_polarity_flip_physical{40.0}=0x1 + +#wc10 P/N flip +phy_chain_tx_polarity_flip_physical{41.0}=0x1 +phy_chain_rx_polarity_flip_physical{41.0}=0x1 +phy_chain_tx_polarity_flip_physical{42.0}=0x0 +phy_chain_rx_polarity_flip_physical{42.0}=0x1 +phy_chain_tx_polarity_flip_physical{43.0}=0x1 +phy_chain_rx_polarity_flip_physical{43.0}=0x0 +phy_chain_tx_polarity_flip_physical{44.0}=0x1 +phy_chain_rx_polarity_flip_physical{44.0}=0x1 + +#wc11 P/N flip +phy_chain_tx_polarity_flip_physical{45.0}=0x1 +phy_chain_rx_polarity_flip_physical{45.0}=0x0 +phy_chain_tx_polarity_flip_physical{46.0}=0x1 +phy_chain_rx_polarity_flip_physical{46.0}=0x0 +phy_chain_tx_polarity_flip_physical{47.0}=0x1 +phy_chain_rx_polarity_flip_physical{47.0}=0x1 +phy_chain_tx_polarity_flip_physical{48.0}=0x0 +phy_chain_rx_polarity_flip_physical{48.0}=0x1 + +#wc12 P/N flip +phy_chain_tx_polarity_flip_physical{49.0}=0x1 +phy_chain_rx_polarity_flip_physical{49.0}=0x0 +phy_chain_tx_polarity_flip_physical{50.0}=0x1 +phy_chain_rx_polarity_flip_physical{50.0}=0x0 +phy_chain_tx_polarity_flip_physical{51.0}=0x0 +phy_chain_rx_polarity_flip_physical{51.0}=0x1 +phy_chain_tx_polarity_flip_physical{52.0}=0x1 +phy_chain_rx_polarity_flip_physical{52.0}=0x1 + +#wc13 P/N flip +phy_chain_tx_polarity_flip_physical{53.0}=0x0 +phy_chain_rx_polarity_flip_physical{53.0}=0x0 +phy_chain_tx_polarity_flip_physical{54.0}=0x1 +phy_chain_rx_polarity_flip_physical{54.0}=0x1 +phy_chain_tx_polarity_flip_physical{55.0}=0x0 +phy_chain_rx_polarity_flip_physical{55.0}=0x1 +phy_chain_tx_polarity_flip_physical{56.0}=0x1 +phy_chain_rx_polarity_flip_physical{56.0}=0x1 + +#wc14 P/N flip +phy_chain_tx_polarity_flip_physical{57.0}=0x1 +phy_chain_rx_polarity_flip_physical{57.0}=0x0 +phy_chain_tx_polarity_flip_physical{58.0}=0x1 +phy_chain_rx_polarity_flip_physical{58.0}=0x1 +phy_chain_tx_polarity_flip_physical{59.0}=0x0 +phy_chain_rx_polarity_flip_physical{59.0}=0x0 +phy_chain_tx_polarity_flip_physical{60.0}=0x1 +phy_chain_rx_polarity_flip_physical{60.0}=0x1 + +#wc15 P/N flip +phy_chain_tx_polarity_flip_physical{61.0}=0x0 +phy_chain_rx_polarity_flip_physical{61.0}=0x1 +phy_chain_tx_polarity_flip_physical{62.0}=0x1 +phy_chain_rx_polarity_flip_physical{62.0}=0x0 +phy_chain_tx_polarity_flip_physical{63.0}=0x0 +phy_chain_rx_polarity_flip_physical{63.0}=0x1 +phy_chain_tx_polarity_flip_physical{64.0}=0x0 +phy_chain_rx_polarity_flip_physical{64.0}=0x0 + +#wc16 P/N flip +phy_chain_tx_polarity_flip_physical{65.0}=0x1 +phy_chain_rx_polarity_flip_physical{65.0}=0x0 +phy_chain_tx_polarity_flip_physical{66.0}=0x0 +phy_chain_rx_polarity_flip_physical{66.0}=0x0 +phy_chain_tx_polarity_flip_physical{67.0}=0x1 +phy_chain_rx_polarity_flip_physical{67.0}=0x1 +phy_chain_tx_polarity_flip_physical{68.0}=0x0 +phy_chain_rx_polarity_flip_physical{68.0}=0x0 + +#wc17 P/N flip +phy_chain_tx_polarity_flip_physical{69.0}=0x1 +phy_chain_rx_polarity_flip_physical{69.0}=0x1 +phy_chain_tx_polarity_flip_physical{70.0}=0x0 +phy_chain_rx_polarity_flip_physical{70.0}=0x0 +phy_chain_tx_polarity_flip_physical{71.0}=0x1 +phy_chain_rx_polarity_flip_physical{71.0}=0x0 +phy_chain_tx_polarity_flip_physical{72.0}=0x0 +phy_chain_rx_polarity_flip_physical{72.0}=0x0 + +#wc18 P/N flip +phy_chain_tx_polarity_flip_physical{73.0}=0x0 +phy_chain_rx_polarity_flip_physical{73.0}=0x1 +phy_chain_tx_polarity_flip_physical{74.0}=0x1 +phy_chain_rx_polarity_flip_physical{74.0}=0x0 +phy_chain_tx_polarity_flip_physical{75.0}=0x0 +phy_chain_rx_polarity_flip_physical{75.0}=0x1 +phy_chain_tx_polarity_flip_physical{76.0}=0x1 +phy_chain_rx_polarity_flip_physical{76.0}=0x0 + +#wc19 P/N flip +phy_chain_tx_polarity_flip_physical{77.0}=0x0 +phy_chain_rx_polarity_flip_physical{77.0}=0x0 +phy_chain_tx_polarity_flip_physical{78.0}=0x0 +phy_chain_rx_polarity_flip_physical{78.0}=0x0 +phy_chain_tx_polarity_flip_physical{79.0}=0x1 +phy_chain_rx_polarity_flip_physical{79.0}=0x1 +phy_chain_tx_polarity_flip_physical{80.0}=0x1 +phy_chain_rx_polarity_flip_physical{80.0}=0x1 + +#wc20 P/N flip +phy_chain_tx_polarity_flip_physical{81.0}=0x0 +phy_chain_rx_polarity_flip_physical{81.0}=0x0 +phy_chain_tx_polarity_flip_physical{82.0}=0x0 +phy_chain_rx_polarity_flip_physical{82.0}=0x0 +phy_chain_tx_polarity_flip_physical{83.0}=0x1 +phy_chain_rx_polarity_flip_physical{83.0}=0x1 +phy_chain_tx_polarity_flip_physical{84.0}=0x1 +phy_chain_rx_polarity_flip_physical{84.0}=0x0 + +#wc21 P/N flip +phy_chain_tx_polarity_flip_physical{85.0}=0x1 +phy_chain_rx_polarity_flip_physical{85.0}=0x1 +phy_chain_tx_polarity_flip_physical{86.0}=0x0 +phy_chain_rx_polarity_flip_physical{86.0}=0x1 +phy_chain_tx_polarity_flip_physical{87.0}=0x1 +phy_chain_rx_polarity_flip_physical{87.0}=0x0 +phy_chain_tx_polarity_flip_physical{88.0}=0x0 +phy_chain_rx_polarity_flip_physical{88.0}=0x0 + +#wc22 P/N flip +phy_chain_tx_polarity_flip_physical{89.0}=0x1 +phy_chain_rx_polarity_flip_physical{89.0}=0x0 +phy_chain_tx_polarity_flip_physical{90.0}=0x0 +phy_chain_rx_polarity_flip_physical{90.0}=0x0 +phy_chain_tx_polarity_flip_physical{91.0}=0x1 +phy_chain_rx_polarity_flip_physical{91.0}=0x1 +phy_chain_tx_polarity_flip_physical{92.0}=0x0 +phy_chain_rx_polarity_flip_physical{92.0}=0x1 + +#wc23 P/N flip +phy_chain_tx_polarity_flip_physical{93.0}=0x1 +phy_chain_rx_polarity_flip_physical{93.0}=0x1 +phy_chain_tx_polarity_flip_physical{94.0}=0x1 +phy_chain_rx_polarity_flip_physical{94.0}=0x1 +phy_chain_tx_polarity_flip_physical{95.0}=0x0 +phy_chain_rx_polarity_flip_physical{95.0}=0x0 +phy_chain_tx_polarity_flip_physical{96.0}=0x0 +phy_chain_rx_polarity_flip_physical{96.0}=0x1 + +#wc24 P/N flip +phy_chain_tx_polarity_flip_physical{97.0}=0x1 +phy_chain_rx_polarity_flip_physical{97.0}=0x1 +phy_chain_tx_polarity_flip_physical{98.0}=0x0 +phy_chain_rx_polarity_flip_physical{98.0}=0x0 +phy_chain_tx_polarity_flip_physical{99.0}=0x1 +phy_chain_rx_polarity_flip_physical{99.0}=0x1 +phy_chain_tx_polarity_flip_physical{100.0}=0x0 +phy_chain_rx_polarity_flip_physical{100.0}=0x0 + +#wc25 P/N flip +phy_chain_tx_polarity_flip_physical{101.0}=0x1 +phy_chain_rx_polarity_flip_physical{101.0}=0x0 +phy_chain_tx_polarity_flip_physical{102.0}=0x0 +phy_chain_rx_polarity_flip_physical{102.0}=0x1 +phy_chain_tx_polarity_flip_physical{103.0}=0x1 +phy_chain_rx_polarity_flip_physical{103.0}=0x0 +phy_chain_tx_polarity_flip_physical{104.0}=0x0 +phy_chain_rx_polarity_flip_physical{104.0}=0x0 + +#wc26 P/N flip +phy_chain_tx_polarity_flip_physical{105.0}=0x1 +phy_chain_rx_polarity_flip_physical{105.0}=0x0 +phy_chain_tx_polarity_flip_physical{106.0}=0x0 +phy_chain_rx_polarity_flip_physical{106.0}=0x1 +phy_chain_tx_polarity_flip_physical{107.0}=0x1 +phy_chain_rx_polarity_flip_physical{107.0}=0x0 +phy_chain_tx_polarity_flip_physical{108.0}=0x0 +phy_chain_rx_polarity_flip_physical{108.0}=0x1 + +#wc27 P/N flip +phy_chain_tx_polarity_flip_physical{109.0}=0x1 +phy_chain_rx_polarity_flip_physical{109.0}=0x1 +phy_chain_tx_polarity_flip_physical{110.0}=0x0 +phy_chain_rx_polarity_flip_physical{110.0}=0x0 +phy_chain_tx_polarity_flip_physical{111.0}=0x1 +phy_chain_rx_polarity_flip_physical{111.0}=0x1 +phy_chain_tx_polarity_flip_physical{112.0}=0x0 +phy_chain_rx_polarity_flip_physical{112.0}=0x0 + +#wc28 P/N flip +phy_chain_tx_polarity_flip_physical{113.0}=0x1 +phy_chain_rx_polarity_flip_physical{113.0}=0x1 +phy_chain_tx_polarity_flip_physical{114.0}=0x0 +phy_chain_rx_polarity_flip_physical{114.0}=0x0 +phy_chain_tx_polarity_flip_physical{115.0}=0x1 +phy_chain_rx_polarity_flip_physical{115.0}=0x0 +phy_chain_tx_polarity_flip_physical{116.0}=0x0 +phy_chain_rx_polarity_flip_physical{116.0}=0x0 + +#wc29 P/N flip +phy_chain_tx_polarity_flip_physical{117.0}=0x1 +phy_chain_rx_polarity_flip_physical{117.0}=0x1 +phy_chain_tx_polarity_flip_physical{118.0}=0x0 +phy_chain_rx_polarity_flip_physical{118.0}=0x0 +phy_chain_tx_polarity_flip_physical{119.0}=0x1 +phy_chain_rx_polarity_flip_physical{119.0}=0x1 +phy_chain_tx_polarity_flip_physical{120.0}=0x0 +phy_chain_rx_polarity_flip_physical{120.0}=0x0 + +#wc30 P/N flip +phy_chain_tx_polarity_flip_physical{121.0}=0x1 +phy_chain_rx_polarity_flip_physical{121.0}=0x0 +phy_chain_tx_polarity_flip_physical{122.0}=0x0 +phy_chain_rx_polarity_flip_physical{122.0}=0x1 +phy_chain_tx_polarity_flip_physical{123.0}=0x1 +phy_chain_rx_polarity_flip_physical{123.0}=0x0 +phy_chain_tx_polarity_flip_physical{124.0}=0x0 +phy_chain_rx_polarity_flip_physical{124.0}=0x1 + +#wc31 P/N flip +phy_chain_tx_polarity_flip_physical{125.0}=0x1 +phy_chain_rx_polarity_flip_physical{125.0}=0x1 +phy_chain_tx_polarity_flip_physical{126.0}=0x0 +phy_chain_rx_polarity_flip_physical{126.0}=0x0 +phy_chain_tx_polarity_flip_physical{127.0}=0x1 +phy_chain_rx_polarity_flip_physical{127.0}=0x1 +phy_chain_tx_polarity_flip_physical{128.0}=0x0 +phy_chain_rx_polarity_flip_physical{128.0}=0x0 + +#MC P/N flip +phy_chain_tx_polarity_flip_physical{129.0}=0x0 +phy_chain_rx_polarity_flip_physical{129.0}=0x0 +phy_chain_tx_polarity_flip_physical{130.0}=0x0 +phy_chain_rx_polarity_flip_physical{130.0}=0x0 +phy_chain_tx_polarity_flip_physical{131.0}=0x0 +phy_chain_rx_polarity_flip_physical{131.0}=0x0 +phy_chain_tx_polarity_flip_physical{132.0}=0x0 +phy_chain_rx_polarity_flip_physical{132.0}=0x0 + +dport_map_port_1=1 +dport_map_port_5=2 +dport_map_port_9=3 +dport_map_port_13=4 +dport_map_port_17=5 +dport_map_port_21=6 +dport_map_port_25=7 +dport_map_port_29=8 +dport_map_port_33=9 +dport_map_port_37=10 +dport_map_port_41=11 +dport_map_port_45=12 +dport_map_port_49=13 +dport_map_port_53=14 +dport_map_port_57=15 +dport_map_port_61=16 +dport_map_port_67=17 +dport_map_port_71=18 +dport_map_port_75=19 +dport_map_port_79=20 +dport_map_port_83=21 +dport_map_port_87=22 +dport_map_port_91=23 +dport_map_port_95=24 +dport_map_port_99=25 +dport_map_port_103=26 +dport_map_port_107=27 +dport_map_port_111=28 +dport_map_port_115=29 +dport_map_port_119=30 +dport_map_port_123=31 +dport_map_port_127=32 +#dport_map_port_66=33 +#dport_map_port_130=34 + +# configuration for 100G optical module +serdes_preemphasis_1=0x164608 +serdes_preemphasis_5=0x164608 +serdes_preemphasis_9=0x164608 +serdes_preemphasis_13=0x134908 +serdes_preemphasis_17=0x134908 +serdes_preemphasis_21=0x134908 +serdes_preemphasis_25=0x124a08 +serdes_preemphasis_29=0x124a08 +serdes_preemphasis_33=0x114b08 +serdes_preemphasis_37=0x114b08 +serdes_preemphasis_41=0x0f4d08 +serdes_preemphasis_45=0x0f4d08 +serdes_preemphasis_49=0x0d4f08 +serdes_preemphasis_53=0x0d4f08 +serdes_preemphasis_57=0x0d4f08 +serdes_preemphasis_61=0x0d4f08 +serdes_preemphasis_67=0x0d4f08 +serdes_preemphasis_71=0x0d4f08 +serdes_preemphasis_75=0x0d4f08 +serdes_preemphasis_79=0x0d4f08 +serdes_preemphasis_83=0x0d4f08 +serdes_preemphasis_87=0x0f4d08 +serdes_preemphasis_91=0x0f4d08 +serdes_preemphasis_95=0x0f4d08 +serdes_preemphasis_99=0x114b08 +serdes_preemphasis_103=0x114b08 +serdes_preemphasis_107=0x114b08 +serdes_preemphasis_111=0x124a08 +serdes_preemphasis_115=0x134908 +serdes_preemphasis_119=0x134908 +serdes_preemphasis_123=0x134908 +serdes_preemphasis_127=0x164608 + +reglist_enable=1 +scache_filename=/tmp/scache +schan_intr_enable=0 +stable_size=0x5500000 +tdma_timeout_usec=3000000 diff --git a/device/celestica/x86_64-cel_seastone_2-r0/custom.bin b/device/celestica/x86_64-cel_seastone_2-r0/custom.bin new file mode 100644 index 0000000000..a9597df309 Binary files /dev/null and b/device/celestica/x86_64-cel_seastone_2-r0/custom.bin differ diff --git a/device/celestica/x86_64-cel_seastone_2-r0/default_sku b/device/celestica/x86_64-cel_seastone_2-r0/default_sku new file mode 100644 index 0000000000..5ed4cdc661 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/default_sku @@ -0,0 +1 @@ +Seastone_2 t1 diff --git a/device/celestica/x86_64-cel_seastone_2-r0/installer.conf b/device/celestica/x86_64-cel_seastone_2-r0/installer.conf new file mode 100644 index 0000000000..3222a45fe1 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/installer.conf @@ -0,0 +1,3 @@ +CONSOLE_DEV=0 +CONSOLE_SPEED=115200 +ONIE_PLATFORM_EXTRA_CMDLINE_LINUX="earlycon=uart8250,mmio,0xdf37b000" \ No newline at end of file diff --git a/device/celestica/x86_64-cel_seastone_2-r0/led_proc_init.soc b/device/celestica/x86_64-cel_seastone_2-r0/led_proc_init.soc new file mode 100644 index 0000000000..a47644c032 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_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 +led auto on; led start + + diff --git a/device/celestica/x86_64-cel_seastone_2-r0/linkscan_fw.bin b/device/celestica/x86_64-cel_seastone_2-r0/linkscan_fw.bin new file mode 100644 index 0000000000..a6a4794ecc Binary files /dev/null and b/device/celestica/x86_64-cel_seastone_2-r0/linkscan_fw.bin differ diff --git a/device/celestica/x86_64-cel_seastone_2-r0/plugins/eeprom.py b/device/celestica/x86_64-cel_seastone_2-r0/plugins/eeprom.py new file mode 100644 index 0000000000..2ae540870c --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/plugins/eeprom.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica Seastone2 +# +# 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, 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_seastone_2-r0/plugins/psuutil.py b/device/celestica/x86_64-cel_seastone_2-r0/plugins/psuutil.py new file mode 100644 index 0000000000..f315e87554 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/plugins/psuutil.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python + +import os.path +import subprocess +import sys +import re + +try: + from sonic_psu.psu_base import PsuBase +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + +class PsuUtil(PsuBase): + """Platform-specific PSUutil class""" + + def __init__(self): + self.ipmi_sensor = "ipmitool sensor" + PsuBase.__init__(self) + + def run_command(self, command): + proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) + (out, err) = proc.communicate() + + if proc.returncode != 0: + sys.exit(proc.returncode) + + return out + + def find_value(self, grep_string): + result = re.search(".+\| (0x\d{2})\d{2}\|.+", grep_string) + if result: + return result.group(1) + else: + return result + + def get_num_psus(self): + """ + Retrieves the number of PSUs available on the device + :return: An integer, the number of PSUs available on the device + """ + return 2 + + def get_psu_status(self, index): + """ + Retrieves the oprational status of power supply unit (PSU) defined + by 1-based index + :param index: An integer, 1-based index of the PSU of which to query status + :return: Boolean, True if PSU is operating properly, False if PSU is faulty + """ + if index is None: + return False + + grep_key = "PSUL_Status" if index == 1 else "PSUR_Status" + grep_string = self.run_command(self.ipmi_sensor + ' | grep '+ grep_key) + status_byte = self.find_value(grep_string) + + if status_byte is None: + return False + + failure_detected = (int(status_byte, 16) >> 1) & 1 + input_lost = (int(status_byte, 16) >> 3) & 1 + if failure_detected or input_lost: + return False + else: + return True + + def get_psu_presence(self, index): + """ + Retrieves the presence status of power supply unit (PSU) defined + by 1-based index + :param index: An integer, 1-based index of the PSU of which to query status + :return: Boolean, True if PSU is plugged, False if not + """ + if index is None: + return False + + grep_key = "PSUL_Status" if index == 1 else "PSUR_Status" + grep_string = self.run_command(self.ipmi_sensor + ' | grep '+ grep_key) + status_byte = self.find_value(grep_string) + + if status_byte is None: + return False + + presence = ( int(status_byte, 16) >> 0 ) & 1 + if presence: + return True + else: + return False diff --git a/device/celestica/x86_64-cel_seastone_2-r0/plugins/sfputil.py b/device/celestica/x86_64-cel_seastone_2-r0/plugins/sfputil.py new file mode 100755 index 0000000000..db3ca9976e --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/plugins/sfputil.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python +# +# Platform-specific SFP transceiver interface for SONiC +# + +try: + import time + from sonic_sfp.sfputilbase import SfpUtilBase +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + + +class SfpUtil(SfpUtilBase): + """Platform-specific SfpUtil class""" + + PORT_START = 1 + PORT_END = 33 + QSFP_PORT_START = 1 + QSFP_PORT_END = 32 + + EEPROM_OFFSET = 1 + PORT_INFO_PATH = '/sys/class/seastone2_fpga' + + _port_name = "" + _port_to_eeprom_mapping = {} + _port_to_i2cbus_mapping = {} + + @property + def port_start(self): + return self.PORT_START + + @property + def port_end(self): + return self.PORT_END + + @property + def qsfp_ports(self): + return range(self.QSFP_PORT_START, self.QSFP_PORT_END + 1) + + @property + def port_to_eeprom_mapping(self): + return self._port_to_eeprom_mapping + + @property + def port_to_i2cbus_mapping(self): + return self._port_to_i2cbus_mapping + + def get_port_name(self, port_num): + if port_num in self.qsfp_ports: + self._port_name = "QSFP" + str(port_num - self.QSFP_PORT_START + 1) + else: + self._port_name = "SFP" + str(port_num) + return self._port_name + + # def get_eeprom_dom_raw(self, port_num): + # if port_num in self.qsfp_ports: + # # QSFP DOM EEPROM is also at addr 0x50 and thus also stored in eeprom_ifraw + # return None + # else: + # # Read dom eeprom at addr 0x51 + # return self._read_eeprom_devid(port_num, self.DOM_EEPROM_ADDR, 256) + + def __init__(self): + # Override port_to_eeprom_mapping for class initialization + eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom' + + for x in range(self.PORT_START, self.PORT_END+1): + self.port_to_i2cbus_mapping[x] = (x + self.EEPROM_OFFSET) + self.port_to_eeprom_mapping[x] = eeprom_path.format( + x + self.EEPROM_OFFSET) + SfpUtilBase.__init__(self) + + def get_presence(self, port_num): + + # Check for invalid port_num + if port_num not in range(self.port_start, self.port_end + 1): + return False + + # Get path for access port presence status + port_name = self.get_port_name(port_num) + sysfs_filename = "qsfp_modprs" if port_num in self.qsfp_ports else "sfp_modabs" + reg_path = "/".join([self.PORT_INFO_PATH, port_name, sysfs_filename]) + + # Read status + try: + reg_file = open(reg_path) + content = reg_file.readline().rstrip() + reg_value = int(content) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + # Module present is active low + if reg_value == 0: + return True + + return False + + def get_low_power_mode(self, port_num): + # Check for invalid QSFP port_num + if port_num not in self.qsfp_ports: + return False + + try: + port_name = self.get_port_name(port_num) + reg_file = open( + "/".join([self.PORT_INFO_PATH, port_name, "qsfp_lpmode"]), "r+") + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + # Read status + content = reg_file.readline().rstrip() + reg_value = int(content, 16) + # ModPrsL is active low + if reg_value == 0: + return False + + return True + + def set_low_power_mode(self, port_num, lpmode): + # Check for invalid QSFP port_num + if port_num not in self.qsfp_ports: + return False + + try: + port_name = self.get_port_name(port_num) + reg_file = open( + "/".join([self.PORT_INFO_PATH, port_name, "qsfp_lpmode"]), "r+") + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + content = hex(lpmode) + + reg_file.seek(0) + reg_file.write(content) + reg_file.close() + + return True + + def reset(self, port_num): + # Check for invalid QSFP port_num + if port_num not in self.qsfp_ports: + return False + + try: + port_name = self.get_port_name(port_num) + reg_file = open( + "/".join([self.PORT_INFO_PATH, port_name, "qsfp_reset"]), "w") + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + # Convert our register value back to a hex string and write back + reg_file.seek(0) + reg_file.write(hex(0)) + reg_file.close() + + # Sleep 1 second to allow it to settle + time.sleep(1) + + # Flip the bit back high and write back to the register to take port out of reset + try: + reg_file = open( + "/".join([self.PORT_INFO_PATH, port_name, "qsfp_reset"]), "w") + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + reg_file.seek(0) + reg_file.write(hex(1)) + reg_file.close() + + return True + + def get_transceiver_change_event(self, timeout=0): + """ + TBD: When the feature request. + """ + raise NotImplementedError + diff --git a/device/celestica/x86_64-cel_seastone_2-r0/pmon_daemon_control.json b/device/celestica/x86_64-cel_seastone_2-r0/pmon_daemon_control.json new file mode 100644 index 0000000000..294259681a --- /dev/null +++ b/device/celestica/x86_64-cel_seastone_2-r0/pmon_daemon_control.json @@ -0,0 +1,6 @@ +{ + "skip_ledd": true, + "skip_xcvrd": false, + "skip_psud": false, + "skip_syseepromd": false +} \ No newline at end of file diff --git a/platform/broadcom/one-image.mk b/platform/broadcom/one-image.mk index 7436fef0ef..b833f9ee2b 100644 --- a/platform/broadcom/one-image.mk +++ b/platform/broadcom/one-image.mk @@ -41,6 +41,7 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \ $(INVENTEC_D6356_PLATFORM_MODULE) \ $(CEL_DX010_PLATFORM_MODULE) \ $(CEL_HALIBURTON_PLATFORM_MODULE) \ + $(CEL_SEASTONE2_PLATFORM_MODULE) \ $(DELTA_AG9032V1_PLATFORM_MODULE) \ $(DELTA_AG9064_PLATFORM_MODULE) \ $(DELTA_AG5648_PLATFORM_MODULE) \ diff --git a/platform/broadcom/platform-modules-cel.mk b/platform/broadcom/platform-modules-cel.mk index b7371e3282..9fe4eb7b69 100644 --- a/platform/broadcom/platform-modules-cel.mk +++ b/platform/broadcom/platform-modules-cel.mk @@ -2,10 +2,12 @@ CEL_DX010_PLATFORM_MODULE_VERSION = 0.9 CEL_HALIBURTON_PLATFORM_MODULE_VERSION = 0.9 +CEL_SEASTONE2_PLATFORM_MODULE_VERSION = 0.9 CEL_SILVERSTONE_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 CEL_DX010_PLATFORM_MODULE = platform-modules-dx010_$(CEL_DX010_PLATFORM_MODULE_VERSION)_amd64.deb @@ -18,6 +20,10 @@ CEL_HALIBURTON_PLATFORM_MODULE = platform-modules-haliburton_$(CEL_HALIBURTON_PL $(CEL_HALIBURTON_PLATFORM_MODULE)_PLATFORM = x86_64-cel_e1031-r0 $(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_HALIBURTON_PLATFORM_MODULE))) +CEL_SEASTONE2_PLATFORM_MODULE = platform-modules-seastone2_$(CEL_SEASTONE2_PLATFORM_MODULE_VERSION)_amd64.deb +$(CEL_SEASTONE2_PLATFORM_MODULE)_PLATFORM = x86_64-cel_seastone_2-r0 +$(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_SEASTONE2_PLATFORM_MODULE))) + CEL_SILVERSTONE_PLATFORM_MODULE = platform-modules-silverstone_$(CEL_SILVERSTONE_PLATFORM_MODULE_VERSION)_amd64.deb $(CEL_SILVERSTONE_PLATFORM_MODULE)_PLATFORM = x86_64-cel_silverstone-r0 $(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_SILVERSTONE_PLATFORM_MODULE))) diff --git a/platform/broadcom/sonic-platform-modules-cel/README.md b/platform/broadcom/sonic-platform-modules-cel/README.md index 26e45c685e..9881d6ecb1 100644 --- a/platform/broadcom/sonic-platform-modules-cel/README.md +++ b/platform/broadcom/sonic-platform-modules-cel/README.md @@ -1 +1 @@ -platform drivers for Celestica DX010 and Haliburton for the SONiC project +platform drivers of Celestica network switchs for the SONiC project diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/control b/platform/broadcom/sonic-platform-modules-cel/debian/control index 2e9b578872..ea39a49e7e 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/control +++ b/platform/broadcom/sonic-platform-modules-cel/debian/control @@ -1,7 +1,7 @@ Source: sonic-cel-platform-modules Section: main Priority: extra -Maintainer: Abhisit Sangjan +Maintainer: Wirut Getbamrung Build-Depends: debhelper (>= 8.0.0), bzip2 Standards-Version: 3.9.3 @@ -16,6 +16,11 @@ Architecture: amd64 Depends: linux-image-4.9.0-9-2-amd64 Description: kernel modules for platform devices such as fan, led, sfp +Package: platform-modules-seastone2 +Architecture: amd64 +Depends: linux-image-4.9.0-9-2-amd64 +Description: kernel modules for platform devices such as led, sfp + Package: platform-modules-silverstone Architecture: amd64 Depends: linux-image-4.9.0-9-2-amd64 diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.init b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.init new file mode 100644 index 0000000000..977cdac060 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.init @@ -0,0 +1,52 @@ +#!/bin/bash + +### BEGIN INIT INFO +# Provides: setup-board +# Required-Start: $portmap +# Required-Stop: +# Should-Start: +# Should-Stop: +# Default-Start: S +# Default-Stop: 0 6 +# Short-Description: Setup seastone2 board. +### END INIT INFO + +case "$1" in +start) + echo -n "Setting up board... " + + # Add driver to support HW + modprobe i2c-dev + modprobe ipmi_devintf + modprobe ipmi_si + modprobe baseboard_cpld + modprobe switchboard_fpga + modprobe mc24lc64t + + # Add driver to support TLV - EEPROM + for devnum in 0 1; do + devname=`cat /sys/bus/i2c/devices/i2c-${devnum}/name` + if [[ $devname == 'SMBus iSMT adapter at '* ]]; then + echo 24lc64t 0x56 > /sys/bus/i2c/devices/i2c-${devnum}/new_device + break + fi + done + sleep 1 + echo "done." + ;; + +stop) + echo "done." + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/platform-modules-seastone2.init {start|stop}" + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.install b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.install new file mode 100644 index 0000000000..e17b5e9cae --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.install @@ -0,0 +1,2 @@ +seastone2/cfg/seastone2-modules.conf etc/modules-load.d +seastone2/systemd/platform-modules-seastone2.service lib/systemd/system diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.postinst b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.postinst new file mode 100644 index 0000000000..9a8e57cb8e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-seastone2.postinst @@ -0,0 +1,3 @@ +depmod -a +systemctl enable platform-modules-seastone2.service +systemctl start platform-modules-seastone2.service \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/rules b/platform/broadcom/sonic-platform-modules-cel/debian/rules index dd5452ccaa..0ed48f9fea 100755 --- a/platform/broadcom/sonic-platform-modules-cel/debian/rules +++ b/platform/broadcom/sonic-platform-modules-cel/debian/rules @@ -5,7 +5,7 @@ export INSTALL_MOD_DIR:=extra KVERSION ?= $(shell uname -r) KERNEL_SRC := /lib/modules/$(KVERSION) MOD_SRC_DIR:= $(shell pwd) -MODULE_DIRS:= dx010 haliburton silverstone +MODULE_DIRS:= dx010 haliburton silverstone seastone2 %: dh $@ diff --git a/platform/broadcom/sonic-platform-modules-cel/seastone2/cfg/seastone2-modules.conf b/platform/broadcom/sonic-platform-modules-cel/seastone2/cfg/seastone2-modules.conf new file mode 100644 index 0000000000..795e6af743 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/seastone2/cfg/seastone2-modules.conf @@ -0,0 +1,16 @@ +# /etc/modules: kernel modules to load at boot time. +# +# This file contains the names of kernel modules that should be loaded +# at boot time, one per line. Lines beginning with "#" are ignored. + +i2c-i801 +i2c-isch +i2c-ismt +i2c-dev +i2c-mux +i2c-smbus + +i2c-mux-gpio +i2c-mux-pca954x +ipmi_devintf +ipmi_si diff --git a/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/Makefile b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/Makefile new file mode 100644 index 0000000000..3b59a78682 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/Makefile @@ -0,0 +1,2 @@ +KBUILD_CFLAGS+=-DSEASTONE2 +obj-m := mc24lc64t.o baseboard_cpld.o switchboard_fpga.o \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/baseboard_cpld.c b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/baseboard_cpld.c new file mode 100644 index 0000000000..07ab42c216 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/baseboard_cpld.c @@ -0,0 +1,378 @@ +/* + * baseboard_cpld.c - driver for Seastone2 Base Board CPLD + * This driver implement sysfs for CPLD register access using LPC bus. + * Copyright (C) 2019 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "baseboard" +/** + * CPLD register address for read and write. + */ +#define VERSION_ADDR 0xA100 +#define SCRATCH_ADDR 0xA101 +#define SYS_LED_ADDR 0xA162 + +#define CPLD_REGISTER_SIZE 0x77 + +struct baseboard_cpld_data { + struct mutex cpld_lock; + uint16_t read_addr; +}; + +struct baseboard_cpld_data *cpld_data; + +/** + * Read the value from scratch register as hex string. + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return Hex string read from scratch register. + */ +static ssize_t scratch_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + mutex_lock(&cpld_data->cpld_lock); + data = inb(SCRATCH_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return sprintf(buf,"0x%2.2x\n", data); +} + +/** + * Set scratch register with specific hex string. + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t scratch_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned long data; + char *last; + + mutex_lock(&cpld_data->cpld_lock); + data = (uint16_t)strtoul(buf,&last,16); + if(data == 0 && buf == last){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + outb(data, SCRATCH_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_RW(scratch); + + +/* CPLD version attributes */ +static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int len = -EIO; + // CPLD register is one byte + mutex_lock(&cpld_data->cpld_lock); + len = sprintf(buf, "0x%2.2x\n",inb(VERSION_ADDR)); + mutex_unlock(&cpld_data->cpld_lock); + return len; +} +static DEVICE_ATTR_RO(version); + + +static ssize_t getreg_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + // CPLD register is one byte + uint16_t addr; + char *last; + + addr = (uint16_t)strtoul(buf,&last,16); + if(addr == 0 && buf == last){ + return -EINVAL; + } + cpld_data->read_addr = addr; + return count; +} + +static ssize_t getreg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int len = -EIO; + // CPLD register is one byte + mutex_lock(&cpld_data->cpld_lock); + len = sprintf(buf, "0x%2.2x\n",inb(cpld_data->read_addr)); + mutex_unlock(&cpld_data->cpld_lock); + return len; +} +static DEVICE_ATTR_RW(getreg); + +static ssize_t setreg_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + // CPLD register is one byte + uint16_t addr; + uint8_t value; + char *tok; + char clone[count]; + char *pclone = clone; + char *last; + + strcpy(clone, buf); + + mutex_lock(&cpld_data->cpld_lock); + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + addr = (uint16_t)strtoul(tok,&last,16); + if(addr == 0 && tok == last){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + value = (uint8_t)strtoul(tok,&last,16); + if(value == 0 && tok == last){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + + outb(value,addr); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_WO(setreg); + +/** + * Show system led status - on/off/1hz/4hz + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return Hex string read from scratch register. + */ +static ssize_t sys_led_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + data = data & 0x3; + return sprintf(buf, "%s\n", + data == 0x03 ? "off" : data == 0x02 ? "4hz" : data ==0x01 ? "1hz": "on"); +} + +/** + * Set the status of system led - on/off/1hz/4hz + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t sys_led_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned char led_status,data; + if(sysfs_streq(buf, "off")){ + led_status = 0x03; + }else if(sysfs_streq(buf, "4hz")){ + led_status = 0x02; + }else if(sysfs_streq(buf, "1hz")){ + led_status = 0x01; + }else if(sysfs_streq(buf, "on")){ + led_status = 0x00; + }else{ + count = -EINVAL; + return count; + } + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + data = data & ~(0x3); + data = data | led_status; + outb(data, SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_RW(sys_led); + +/** + * Show system led color - both/green/yellow/none + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return Hex string read from scratch register. + */ +static ssize_t sys_led_color_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + data = (data >> 4) & 0x3; + return sprintf(buf, "%s\n", + data == 0x03 ? "off" : data == 0x02 ? "yellow" : data ==0x01 ? "green": "both"); +} + +/** + * Set the color of system led - both/green/yellow/none + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t sys_led_color_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned char led_status,data; + if(sysfs_streq(buf, "off")){ + led_status = 0x03; + }else if(sysfs_streq(buf, "yellow")){ + led_status = 0x02; + }else if(sysfs_streq(buf, "green")){ + led_status = 0x01; + }else if(sysfs_streq(buf, "both")){ + led_status = 0x00; + }else{ + count = -EINVAL; + return count; + } + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + data = data & ~( 0x3 << 4); + data = data | (led_status << 4); + outb(data, SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_RW(sys_led_color); + +static struct attribute *baseboard_cpld_attrs[] = { + &dev_attr_version.attr, + &dev_attr_scratch.attr, + &dev_attr_getreg.attr, + &dev_attr_setreg.attr, + &dev_attr_sys_led.attr, + &dev_attr_sys_led_color.attr, + NULL, +}; + +static struct attribute_group baseboard_cpld_attrs_grp = { + .attrs = baseboard_cpld_attrs, +}; + +static struct resource baseboard_cpld_resources[] = { + { + .start = 0xA100, + .end = 0xA1FF, + .flags = IORESOURCE_IO, + }, +}; + +static void baseboard_cpld_dev_release( struct device * dev) +{ + return; +} + +static struct platform_device baseboard_cpld_dev = { + .name = DRIVER_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(baseboard_cpld_resources), + .resource = baseboard_cpld_resources, + .dev = { + .release = baseboard_cpld_dev_release, + } +}; + +static int baseboard_cpld_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret =0; + + cpld_data = devm_kzalloc(&pdev->dev, sizeof(struct baseboard_cpld_data), + GFP_KERNEL); + if (!cpld_data) + return -ENOMEM; + + mutex_init(&cpld_data->cpld_lock); + + cpld_data->read_addr = VERSION_ADDR; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (unlikely(!res)) { + printk(KERN_ERR "Specified Resource Not Available...\n"); + return -1; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &baseboard_cpld_attrs_grp); + if (ret) { + printk(KERN_ERR "Cannot create sysfs for baseboard CPLD\n"); + } + return 0; +} + +static int baseboard_cpld_drv_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &baseboard_cpld_attrs_grp); + return 0; +} + +static struct platform_driver baseboard_cpld_drv = { + .probe = baseboard_cpld_drv_probe, + .remove = __exit_p(baseboard_cpld_drv_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +int baseboard_cpld_init(void) +{ + // Register platform device and platform driver + platform_device_register(&baseboard_cpld_dev); + platform_driver_register(&baseboard_cpld_drv); + return 0; +} + +void baseboard_cpld_exit(void) +{ + // Unregister platform device and platform driver + platform_driver_unregister(&baseboard_cpld_drv); + platform_device_unregister(&baseboard_cpld_dev); +} + +module_init(baseboard_cpld_init); +module_exit(baseboard_cpld_exit); + +MODULE_AUTHOR("Pradchaya Phucharoen "); +MODULE_DESCRIPTION("Celestica Seastone2 Baseboard CPLD Driver"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/mc24lc64t.c b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/mc24lc64t.c new file mode 100644 index 0000000000..fc15bb74b6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/mc24lc64t.c @@ -0,0 +1,174 @@ +/* + * mc24lc64t.c - driver for Microchip 24LC64T + * + * Copyright (C) 2017 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define EEPROM_SIZE 8192 //mc24lt64t eeprom size in bytes. + +struct mc24lc64t_data { + struct mutex update_lock; +}; + +static ssize_t mc24lc64t_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = kobj_to_i2c_client(kobj); + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + unsigned long timeout, read_time, i = 0; + int status; + + mutex_lock(&drvdata->update_lock); + + if (i2c_smbus_write_byte_data(client, off>>8, off)) + { + status = -EIO; + goto exit; + } + + msleep(1); + +begin: + + if (i < count) + { + timeout = jiffies + msecs_to_jiffies(25); /* 25 mS timeout*/ + do { + read_time = jiffies; + + status = i2c_smbus_read_byte(client); + if (status >= 0) + { + buf[i++] = status; + goto begin; + } + } while (time_before(read_time, timeout)); + + status = -ETIMEDOUT; + goto exit; + } + + status = count; + +exit: + mutex_unlock(&drvdata->update_lock); + + return status; +} + + +static ssize_t mc24lc64t_write (struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count){ + + struct i2c_client *client = kobj_to_i2c_client(kobj); + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + unsigned long timeout, write_time, i = 0; + int status; + u16 value; + + mutex_lock(&drvdata->update_lock); + +begin: + if (i < count){ + timeout = jiffies + msecs_to_jiffies(25); /* 25 mS timeout*/ + value = (buf[i] << 8 | ( off &0xff)); + do { + write_time = jiffies; + status = i2c_smbus_write_word_data(client, off>>8, value); + if (status >= 0) + { + // increase offset + off++; + // increase buffer index + i++; + goto begin; + } + } while (time_before(write_time, timeout)); + status = -ETIMEDOUT; + goto exit; + } + status = count; + +exit: + mutex_unlock(&drvdata->update_lock); + return status; +} + + +static struct bin_attribute mc24lc64t_bit_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO | S_IWUGO, + }, + .size = EEPROM_SIZE, + .read = mc24lc64t_read, + .write = mc24lc64t_write, +}; + +static int mc24lc64t_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct mc24lc64t_data *drvdata; + int err; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA + | I2C_FUNC_SMBUS_READ_BYTE)) + return -EPFNOSUPPORT; + + if (!(drvdata = devm_kzalloc(&client->dev, + sizeof(struct mc24lc64t_data), GFP_KERNEL))) + return -ENOMEM; + + i2c_set_clientdata(client, drvdata); + mutex_init(&drvdata->update_lock); + + err = sysfs_create_bin_file(&client->dev.kobj, &mc24lc64t_bit_attr); + return err; +} + +static int mc24lc64t_remove(struct i2c_client *client) +{ + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + sysfs_remove_bin_file(&client->dev.kobj, &mc24lc64t_bit_attr); + + return 0; +} + +static const struct i2c_device_id mc24lc64t_id[] = { + { "24lc64t", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mc24lc64t_id); + +static struct i2c_driver mc24lc64t_driver = { + .driver = { + .name = "mc24lc64t", + .owner = THIS_MODULE, + }, + .probe = mc24lc64t_probe, + .remove = mc24lc64t_remove, + .id_table = mc24lc64t_id, +}; + +module_i2c_driver(mc24lc64t_driver); + +MODULE_AUTHOR("Abhisit Sangjan "); +MODULE_DESCRIPTION("Microchip 24LC64T Driver"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/switchboard_fpga.c b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/switchboard_fpga.c new file mode 100644 index 0000000000..c7a86e13d3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/seastone2/modules/switchboard_fpga.c @@ -0,0 +1,2089 @@ +/* + * switchboard_fpga.c - driver for seastone2 Switch board FPGA/CPLD. + * + * Author: Pradchaya Phucharoen + * + * Copyright (C) 2019 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * / + * \--sys + * \--devices + * \--platform + * \--switchboard + * |--FPGA + * |--CPLD1 + * |--CPLD2 + * \--SFF + * |--QSFP[1..32] + * \--SFP1 + * + */ + +#ifndef TEST_MODE +#define MOD_VERSION "2.1.3" +#else +#define MOD_VERSION "TEST" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +static int majorNumber; + +#define CLASS_NAME "seastone2_fpga" +#define DRIVER_NAME "switchboard" +#define FPGA_PCI_NAME "Seastone2_fpga_pci" +#define DEVICE_NAME "fwupgrade" + + +static int smbus_access(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data); + +static int fpga_i2c_access(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data); + +static int fpgafw_init(void); +static void fpgafw_exit(void); + + +/* +======================================== +FPGA PCIe BAR 0 Registers +======================================== +Misc Control 0x00000000 – 0x000000FF. +I2C_CH1 0x00000100 - 0x00000110 +I2C_CH2 0x00000200 - 0x00000210. +I2C_CH3 0x00000300 - 0x00000310. +I2C_CH4 0x00000400 - 0x00000410. +I2C_CH5 0x00000500 - 0x00000510. +I2C_CH6 0x00000600 - 0x00000610. +I2C_CH7 0x00000700 - 0x00000710. +I2C_CH8 0x00000800 - 0x00000810. +I2C_CH9 0x00000900 - 0x00000910. +I2C_CH10 0x00000A00 - 0x00000A10. +SPI Master 0x00001200 - 0x00001300. +PORT XCVR 0x00004000 - 0x00004FFF. +*/ + +/* MISC */ +#define FPGA_VERSION 0x0000 +#define FPGA_VERSION_MJ_MSK 0xff00 +#define FPGA_VERSION_MN_MSK 0x00ff +#define FPGA_SCRATCH 0x0004 +#define FPGA_BROAD_TYPE 0x0008 +#define FPGA_BROAD_REV_MSK 0x0038 +#define FPGA_BROAD_ID_MSK 0x0007 +#define FPGA_PLL_STATUS 0x0014 +#define BMC_I2C_SCRATCH 0x0020 +#define FPGA_SLAVE_CPLD_REST 0x0030 +#define FPGA_PERIPH_RESET_CTRL 0x0034 +#define FPGA_INT_STATUS 0x0040 +#define FPGA_INT_SRC_STATUS 0x0044 +#define FPGA_INT_FLAG 0x0048 +#define FPGA_INT_MASK 0x004c +#define FPGA_MISC_CTRL 0x0050 +#define FPGA_MISC_STATUS 0x0054 +#define FPGA_AVS_VID_STATUS 0x0068 +#define FPGA_FEATURE_CARD_GPIO 0x0070 +#define FPGA_PORT_XCVR_READY 0x000c + +/* I2C_MASTER BASE ADDR */ +#define I2C_MASTER_FREQ_1 0x0100 +#define I2C_MASTER_CTRL_1 0x0104 +#define I2C_MASTER_STATUS_1 0x0108 +#define I2C_MASTER_DATA_1 0x010c +#define I2C_MASTER_PORT_ID_1 0x0110 +#define I2C_MASTER_CH_1 1 +#define I2C_MASTER_CH_2 2 +#define I2C_MASTER_CH_3 3 +#define I2C_MASTER_CH_4 4 +#define I2C_MASTER_CH_5 5 +#define I2C_MASTER_CH_6 6 +#define I2C_MASTER_CH_7 7 +#define I2C_MASTER_CH_8 8 +#define I2C_MASTER_CH_9 9 +#define I2C_MASTER_CH_10 10 +#define I2C_MASTER_CH_TOTAL I2C_MASTER_CH_10 + +/* SPI_MASTER */ +#define SPI_MASTER_WR_EN 0x1200 /* one bit */ +#define SPI_MASTER_WR_DATA 0x1204 /* 32 bits */ +#define SPI_MASTER_CHK_ID 0x1208 /* one bit */ +#define SPI_MASTER_VERIFY 0x120c /* one bit */ +#define SPI_MASTER_STATUS 0x1210 /* 15 bits */ +#define SPI_MASTER_MODULE_RST 0x1214 /* one bit */ + +/* FPGA FRONT PANEL PORT MGMT */ +#define SFF_PORT_CTRL_BASE 0x4000 +#define SFF_PORT_STATUS_BASE 0x4004 +#define SFF_PORT_INT_STATUS_BASE 0x4008 +#define SFF_PORT_INT_MASK_BASE 0x400c + +#define PORT_XCVR_REGISTER_SIZE 0x1000 + +/* PORT CTRL REGISTER +[31:7] RSVD +[6] LPMOD 6 +[5] RSVD +[4] RST 4 +[3:1] RSVD +[0] TXDIS 0 +*/ +#define CTRL_LPMOD 6 +#define CTRL_RST 4 +#define CTRL_TXDIS 0 + +/* PORT STATUS REGISTER +[31:6] RSVD +[5] IRQ 5 +[4] PRESENT 4 +[3] RSVD +[2] TXFAULT 2 +[1] RXLOS 1 +[0] MODABS 0 +*/ +#define STAT_IRQ 5 +#define STAT_PRESENT 4 +#define STAT_TXFAULT 2 +#define STAT_RXLOS 1 +#define STAT_MODABS 0 + +/* PORT INTRPT REGISTER +[31:6] RSVD +[5] INT_N 5 +[4] PRESENT 4 +[3] RSVD +[2] RSVD +[1] RXLOS 1 +[0] MODABS 0 +*/ +#define INTR_INT_N 5 +#define INTR_PRESENT 4 +#define INTR_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 + * + */ + + +#define VIRTUAL_I2C_QSFP_PORT 32 +#define VIRTUAL_I2C_SFP_PORT 1 + +#define SFF_PORT_TOTAL VIRTUAL_I2C_QSFP_PORT + VIRTUAL_I2C_SFP_PORT + +#define VIRTUAL_I2C_BUS_OFFSET 2 +#define CPLD1_SLAVE_ADDR 0x30 +#define CPLD2_SLAVE_ADDR 0x31 + +static struct class* fpgafwclass = NULL; ///< The device-driver class struct pointer +static struct device* fpgafwdev = NULL; ///< The device-driver device struct pointer + +#define PCI_VENDOR_ID_TEST 0x1af4 + +#ifndef PCI_VENDOR_ID_XILINX +#define PCI_VENDOR_ID_XILINX 0x10EE +#endif + +#define FPGA_PCIE_DEVICE_ID 0x7021 +#define TEST_PCIE_DEVICE_ID 0x1110 + + +#ifdef DEBUG_KERN +#define info(fmt,args...) printk(KERN_INFO "line %3d : "fmt,__LINE__,##args) +#define check(REG) printk(KERN_INFO "line %3d : %-8s = %2.2X",__LINE__,#REG,ioread8(REG)); +#else +#define info(fmt,args...) +#define check(REG) +#endif + +#define GET_REG_BIT(REG,BIT) ((ioread8(REG) >> BIT) & 0x01) +#define SET_REG_BIT_H(REG,BIT) iowrite8(ioread8(REG) | (0x01 << BIT),REG) +#define SET_REG_BIT_L(REG,BIT) iowrite8(ioread8(REG) & ~(0x01 << BIT),REG) + +static struct mutex fpga_i2c_master_locks[I2C_MASTER_CH_TOTAL]; +/* Store lasted switch address and channel */ +static uint16_t fpga_i2c_lasted_access_port[I2C_MASTER_CH_TOTAL]; + +enum PORT_TYPE { + NONE, + QSFP, + SFP +}; + +struct i2c_switch{ + unsigned char master_bus; // I2C bus number + unsigned char switch_addr; // PCA9548 device address, 0xFF if directly connect to a bus. + unsigned char channel; // PCA9548 channel number. If the switch_addr is 0xFF, this value is ignored. + enum PORT_TYPE port_type; // QSFP/SFP tranceiver port type. + char calling_name[20]; // Calling name. +}; + +struct i2c_dev_data { + int portid; + struct i2c_switch pca9548; +}; + +/* PREDEFINED I2C SWITCH DEVICE TOPOLOGY */ +static struct i2c_switch fpga_i2c_bus_dev[] = { + /* BUS2 QSFP Exported as virtual bus */ + {I2C_MASTER_CH_2,0x72,0,QSFP,"QSFP1"}, {I2C_MASTER_CH_2,0x72,1,QSFP,"QSFP2"}, + {I2C_MASTER_CH_2,0x72,2,QSFP,"QSFP3"}, {I2C_MASTER_CH_2,0x72,3,QSFP,"QSFP4"}, + {I2C_MASTER_CH_2,0x72,4,QSFP,"QSFP5"}, {I2C_MASTER_CH_2,0x72,5,QSFP,"QSFP6"}, + {I2C_MASTER_CH_2,0x72,6,QSFP,"QSFP7"}, {I2C_MASTER_CH_2,0x72,7,QSFP,"QSFP8"}, + {I2C_MASTER_CH_2,0x73,0,QSFP,"QSFP9"}, {I2C_MASTER_CH_2,0x73,1,QSFP,"QSFP10"}, + {I2C_MASTER_CH_2,0x73,2,QSFP,"QSFP11"},{I2C_MASTER_CH_2,0x73,3,QSFP,"QSFP12"}, + {I2C_MASTER_CH_2,0x73,4,QSFP,"QSFP13"},{I2C_MASTER_CH_2,0x73,5,QSFP,"QSFP14"}, + {I2C_MASTER_CH_2,0x73,6,QSFP,"QSFP15"},{I2C_MASTER_CH_2,0x73,7,QSFP,"QSFP16"}, + {I2C_MASTER_CH_2,0x74,0,QSFP,"QSFP17"},{I2C_MASTER_CH_2,0x74,1,QSFP,"QSFP18"}, + {I2C_MASTER_CH_2,0x74,2,QSFP,"QSFP19"},{I2C_MASTER_CH_2,0x74,3,QSFP,"QSFP20"}, + {I2C_MASTER_CH_2,0x74,4,QSFP,"QSFP21"},{I2C_MASTER_CH_2,0x74,5,QSFP,"QSFP22"}, + {I2C_MASTER_CH_2,0x74,6,QSFP,"QSFP23"},{I2C_MASTER_CH_2,0x74,7,QSFP,"QSFP24"}, + {I2C_MASTER_CH_2,0x75,0,QSFP,"QSFP25"},{I2C_MASTER_CH_2,0x75,1,QSFP,"QSFP26"}, + {I2C_MASTER_CH_2,0x75,2,QSFP,"QSFP27"},{I2C_MASTER_CH_2,0x75,3,QSFP,"QSFP28"}, + {I2C_MASTER_CH_2,0x75,4,QSFP,"QSFP29"},{I2C_MASTER_CH_2,0x75,5,QSFP,"QSFP30"}, + {I2C_MASTER_CH_2,0x75,6,QSFP,"QSFP31"},{I2C_MASTER_CH_2,0x75,7,QSFP,"QSFP32"}, + /* BUS1 SFP+ Exported as virtual bus */ + {I2C_MASTER_CH_1,0x72,0,SFP,"SFP1"}, + /* BUS3 Switchboard CPLD */ + {I2C_MASTER_CH_3,0xFF,0,NONE,"I2C_3"}, +}; + +#define VIRTUAL_I2C_PORT_LENGTH ARRAY_SIZE(fpga_i2c_bus_dev) +#define VIRTUAL_I2C_CPLD_INDEX SFF_PORT_TOTAL + +struct fpga_device{ + /* data mmio region */ + void __iomem *data_base_addr; + resource_size_t data_mmio_start; + resource_size_t data_mmio_len; +}; + +static struct fpga_device fpga_dev = { + .data_base_addr = 0, + .data_mmio_start = 0, + .data_mmio_len = 0, +}; + +struct seastone2_fpga_data { + struct device *sff_devices[SFF_PORT_TOTAL]; + struct i2c_client *sff_i2c_clients[SFF_PORT_TOTAL]; + struct i2c_adapter *i2c_adapter[VIRTUAL_I2C_PORT_LENGTH]; + struct mutex fpga_lock; // For FPGA internal lock + void __iomem * fpga_read_addr; + uint8_t cpld1_read_addr; + uint8_t cpld2_read_addr; +}; + +struct sff_device_data { + int portid; + enum PORT_TYPE port_type; +}; + +struct seastone2_fpga_data *fpga_data; + +/* + * Kernel object for other module drivers. + * Other module can use these kobject as a parent. + */ + +static struct kobject *fpga = NULL; +static struct kobject *cpld1 = NULL; +static struct kobject *cpld2 = NULL; + +/** + * Device node in sysfs tree. + */ +static struct device *sff_dev = NULL; + +/** + * [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; +} + +/** + * Show FPGA port XCVR ready status + */ +static ssize_t ready_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u32 data; + unsigned int REGISTER = FPGA_PORT_XCVR_READY; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr+REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> 0) & 1U); +} + +/* FPGA attributes */ +static DEVICE_ATTR( getreg, 0600, get_fpga_reg_value, set_fpga_reg_address); +static DEVICE_ATTR( scratch, 0600, get_fpga_scratch, set_fpga_scratch); +static DEVICE_ATTR( setreg, 0200, NULL , set_fpga_reg_value); +static DEVICE_ATTR_RO(ready); + +static struct attribute *fpga_attrs[] = { + &dev_attr_getreg.attr, + &dev_attr_scratch.attr, + &dev_attr_setreg.attr, + &dev_attr_ready.attr, + NULL, +}; + +static struct attribute_group fpga_attr_grp = { + .attrs = fpga_attrs, +}; + +/* SW CPLDs attributes */ +static ssize_t cpld1_getreg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + uint8_t data; + 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_getreg_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_getreg = __ATTR(getreg,0600,cpld1_getreg_show,cpld1_getreg_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 -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],CPLD1_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_cpld1_setreg = __ATTR(setreg,0200,NULL,cpld1_setreg_store); + +static struct attribute *cpld1_attrs[] = { + &dev_attr_cpld1_getreg.attr, + &dev_attr_cpld1_scratch.attr, + &dev_attr_cpld1_setreg.attr, + NULL, +}; + +static struct attribute_group cpld1_attr_grp = { + .attrs = cpld1_attrs, +}; + +static ssize_t cpld2_getreg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + uint8_t data; + 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_getreg_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_getreg = __ATTR(getreg,0600,cpld2_getreg_show,cpld2_getreg_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_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 = -EINVAL; + return error; + } + + REG_FDR0 = I2C_MASTER_FREQ_1 + (master_bus-1)*0x0100; + REG_CR0 = I2C_MASTER_CTRL_1 + (master_bus-1)*0x0100; + REG_SR0 = I2C_MASTER_STATUS_1 + (master_bus-1)*0x0100; + REG_DR0 = I2C_MASTER_DATA_1 + (master_bus-1)*0x0100; + REG_ID0 = I2C_MASTER_PORT_ID_1 + (master_bus-1)*0x0100; + + check(pci_bar+REG_SR0); + check(pci_bar+REG_CR0); + + timeout = jiffies + msecs_to_jiffies(timeout); + while(1){ + Status = ioread8(pci_bar+REG_SR0); + if(jiffies > timeout){ + info("Status %2.2X",Status); + info("Error Timeout"); + error = -ETIMEDOUT; + break; + } + + + if(Status & (1 << I2C_SR_BIT_MIF)){ + break; + } + + if(writing == 0 && (Status & (1<portid; + pci_bar = fpga_dev.data_base_addr; + +#ifdef DEBUG_KERN + printk(KERN_INFO "portid %2d|@ 0x%2.2X|f 0x%4.4X|(%d)%-5s| (%d)%-15s|CMD %2.2X " + ,portid,addr,flags,rw,rw == 1 ? "READ ":"WRITE" + ,size, size == 0 ? "QUICK" : + size == 1 ? "BYTE" : + size == 2 ? "BYTE_DATA" : + size == 3 ? "WORD_DATA" : + size == 4 ? "PROC_CALL" : + size == 5 ? "BLOCK_DATA" : + size == 8 ? "I2C_BLOCK_DATA" : "ERROR" + ,cmd); +#endif + /* Map the size to what the chip understands */ + switch (size) { + case I2C_SMBUS_QUICK: + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: + break; + default: + printk(KERN_INFO "Unsupported transaction %d\n", size); + error = -EOPNOTSUPP; + goto Done; + } + + master_bus = dev_data->pca9548.master_bus; + + if(master_bus < I2C_MASTER_CH_1 || master_bus > I2C_MASTER_CH_TOTAL){ + error = -ENXIO; + goto Done; + } + + REG_FDR0 = I2C_MASTER_FREQ_1 + (master_bus-1)*0x0100; + REG_CR0 = I2C_MASTER_CTRL_1 + (master_bus-1)*0x0100; + REG_SR0 = I2C_MASTER_STATUS_1 + (master_bus-1)*0x0100; + REG_DR0 = I2C_MASTER_DATA_1 + (master_bus-1)*0x0100; + REG_ID0 = I2C_MASTER_PORT_ID_1 + (master_bus-1)*0x0100; + + iowrite8(portid,pci_bar+REG_ID0); + + ////[S][ADDR/R] + // Clear status register + iowrite8(0,pci_bar+REG_SR0); + iowrite8(1 << I2C_CR_BIT_MIEN | 1 << I2C_CR_BIT_MTX | 1 << I2C_CR_BIT_MSTA ,pci_bar+REG_CR0); + SET_REG_BIT_H(pci_bar+REG_CR0,I2C_CR_BIT_MEN); + + if(rw == I2C_SMBUS_READ && + (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE)){ + // sent device address with Read mode + iowrite8(addr << 1 | 0x01,pci_bar+REG_DR0); + }else{ + // sent device address with Write mode + iowrite8(addr << 1 | 0x00,pci_bar+REG_DR0); + } + + + + info( "MS Start"); + + //// Wait {A} + error = i2c_wait_ack(adapter,12,1); + if(error<0){ + info( "get error %d",error); + goto Done; + } + + //// [CMD]{A} + if(size == I2C_SMBUS_BYTE_DATA || + size == I2C_SMBUS_WORD_DATA || + size == I2C_SMBUS_BLOCK_DATA || + size == I2C_SMBUS_I2C_BLOCK_DATA || + (size == I2C_SMBUS_BYTE && rw == I2C_SMBUS_WRITE)){ + + // sent command code to data register + iowrite8(cmd,pci_bar+REG_DR0); + info( "MS Send CMD 0x%2.2X",cmd); + + // Wait {A} + error = i2c_wait_ack(adapter,12,1); + if(error<0){ + info( "get error %d",error); + goto Done; + } + } + + switch(size){ + case I2C_SMBUS_BYTE_DATA: + cnt = 1; break; + case I2C_SMBUS_WORD_DATA: + cnt = 2; break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: + /* In block data modes keep number of byte in block[0] */ + cnt = data->block[0]; + break; + default: + cnt = 0; break; + } + + // [CNT] used only block data write + if(size == I2C_SMBUS_BLOCK_DATA && rw == I2C_SMBUS_WRITE){ + + iowrite8(cnt,pci_bar+REG_DR0); + info( "MS Send CNT 0x%2.2X",cnt); + + // Wait {A} + error = i2c_wait_ack(adapter,12,1); + if(error<0){ + info( "get error %d",error); + goto Done; + } + } + + // [DATA]{A} + if( rw == I2C_SMBUS_WRITE && ( + size == I2C_SMBUS_BYTE || + size == I2C_SMBUS_BYTE_DATA || + size == I2C_SMBUS_WORD_DATA || + size == I2C_SMBUS_BLOCK_DATA || + size == I2C_SMBUS_I2C_BLOCK_DATA + )){ + bid = 0; + info( "MS prepare to sent [%d bytes]",cnt); + if(size == I2C_SMBUS_BLOCK_DATA || size == I2C_SMBUS_I2C_BLOCK_DATA){ + bid=1; // block[0] is cnt; + cnt+=1; // offset from block[0] + } + for(;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; + } + + 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; + } + } + } + } + + // [P] + SET_REG_BIT_L(pci_bar+REG_CR0,I2C_CR_BIT_MSTA); + info( "MS STOP"); + +Done: + iowrite8(1<pca9548.master_bus; + switch_addr = dev_data->pca9548.switch_addr; + channel = dev_data->pca9548.channel; + + // Acquire the master resource. + mutex_lock(&fpga_i2c_master_locks[master_bus - 1]); + prev_port = fpga_i2c_lasted_access_port[master_bus - 1]; + prev_switch = (unsigned char)(prev_port >> 8) & 0xFF; + prev_ch = (unsigned char)(prev_port & 0xFF); + + if (switch_addr != 0xFF) { + + // Check lasted access switch address on a master + if ( prev_switch != switch_addr && prev_switch != 0 ) { + // reset prev_port PCA9548 chip + retry = 3; + while(retry--){ + error = smbus_access(adapter, (u16)(prev_switch), flags, I2C_SMBUS_WRITE, 0x00, I2C_SMBUS_BYTE, NULL); + if(error >= 0){ + break; + }else{ + dev_dbg(&adapter->dev,"Failed to deselect ch %d of 0x%x, CODE %d\n", prev_ch, prev_switch, error); + } + } + if(retry < 0){ + goto release_unlock; + } + // set PCA9548 to current channel + retry = 3; + while(retry--){ + error = smbus_access(adapter, switch_addr, flags, I2C_SMBUS_WRITE, 1 << channel, I2C_SMBUS_BYTE, NULL); + if(error >= 0){ + break; + }else{ + dev_dbg(&adapter->dev,"Failed to deselect ch %d of 0x%x, CODE %d\n", prev_ch, prev_switch, error); + } + } + if(retry < 0){ + goto release_unlock; + } + // update lasted port + fpga_i2c_lasted_access_port[master_bus - 1] = switch_addr << 8 | channel; + + } else { + // check if channel is also changes + if ( prev_ch != channel || prev_switch == 0 ) { + // set new PCA9548 at switch_addr to current + retry = 3; + while(retry--){ + error = smbus_access(adapter, switch_addr, flags, I2C_SMBUS_WRITE, 1 << channel, I2C_SMBUS_BYTE, NULL); + if(error >= 0){ + break; + }else{ + dev_dbg(&adapter->dev,"Failed to deselect ch %d of 0x%x, CODE %d\n", prev_ch, prev_switch, error); + } + } + if(retry < 0){ + goto release_unlock; + } + // update lasted port + fpga_i2c_lasted_access_port[master_bus - 1] = switch_addr << 8 | channel; + } + } + } + + // Do SMBus communication + error = smbus_access(adapter, addr, flags, rw, cmd, size, data); + if(error < 0){ + dev_dbg( &adapter->dev,"smbus_xfer failed (%d) @ 0x%2.2X|f 0x%4.4X|(%d)%-5s| (%d)%-10s|CMD %2.2X " + , error, addr, flags, rw, rw == 1 ? "READ " : "WRITE" + , size, size == 0 ? "QUICK" : + size == 1 ? "BYTE" : + size == 2 ? "BYTE_DATA" : + size == 3 ? "WORD_DATA" : + size == 4 ? "PROC_CALL" : + size == 5 ? "BLOCK_DATA" : + size == 8 ? "I2C_BLOCK_DATA" : "ERROR" + , cmd); + } + +release_unlock: + mutex_unlock(&fpga_i2c_master_locks[master_bus - 1]); + dev_dbg(&adapter->dev,"switch ch %d of 0x%x -> ch %d of 0x%x\n", prev_ch, prev_switch, channel, switch_addr); + return error; +} + + + +/** + * A callback function show available smbus functions. + */ +static u32 fpga_i2c_func(struct i2c_adapter *a) +{ + return I2C_FUNC_SMBUS_QUICK | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA| + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm seastone2_i2c_algorithm = { + .smbus_xfer = fpga_i2c_access, + .functionality = fpga_i2c_func, +}; + +/** + * Create virtual I2C bus adapter for switch devices + * @param pdev platform device pointer + * @param portid virtual i2c port id for switch device mapping + * @param bus_number_offset bus offset for virtual i2c adapter in system + * @return i2c adapter. + * + * When bus_number_offset is -1, created adapter with dynamic bus number. + * Otherwise create adapter at i2c bus = bus_number_offset + portid. + */ +static struct i2c_adapter * seastone2_i2c_init(struct platform_device *pdev, int portid, int bus_number_offset) +{ + int error; + struct i2c_adapter *new_adapter; + struct i2c_dev_data *new_data; + void __iomem *i2c_freq_base_reg; + + new_adapter = kzalloc(sizeof(*new_adapter), GFP_KERNEL); + if (!new_adapter){ + printk(KERN_ALERT "Cannot alloc i2c adapter for %s", fpga_i2c_bus_dev[portid].calling_name); + return NULL; + } + + new_adapter->owner = THIS_MODULE; + new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + new_adapter->algo = &seastone2_i2c_algorithm; + /* If the bus offset is -1, use dynamic bus number */ + if (bus_number_offset == -1){ + new_adapter->nr = -1; + }else{ + new_adapter->nr = bus_number_offset + portid; + } + + new_data = kzalloc(sizeof(*new_data), GFP_KERNEL); + if (!new_data){ + printk(KERN_ALERT "Cannot alloc i2c data for %s", fpga_i2c_bus_dev[portid].calling_name); + kzfree(new_adapter); + return NULL; + } + + new_data->portid = portid; + new_data->pca9548.master_bus = fpga_i2c_bus_dev[portid].master_bus; + new_data->pca9548.switch_addr = fpga_i2c_bus_dev[portid].switch_addr; + new_data->pca9548.channel = fpga_i2c_bus_dev[portid].channel; + strcpy(new_data->pca9548.calling_name,fpga_i2c_bus_dev[portid].calling_name); + + snprintf(new_adapter->name, sizeof(new_adapter->name), + "SMBus I2C Adapter PortID: %s", new_data->pca9548.calling_name); + + i2c_freq_base_reg = fpga_dev.data_base_addr+I2C_MASTER_FREQ_1; + iowrite8(0x07,i2c_freq_base_reg+(new_data->pca9548.master_bus-1)*0x100); // 0x07 400kHz + i2c_set_adapdata(new_adapter,new_data); + error = i2c_add_numbered_adapter(new_adapter); + if(error < 0){ + printk(KERN_ALERT "Cannot add i2c adapter %s", new_data->pca9548.calling_name); + kzfree(new_adapter); + kzfree(new_data); + return NULL; + } + + return new_adapter; +}; + +// 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; + struct sff_device_data *sff_data; + + /* The device class need to be instantiated before this function called */ + BUG_ON(fpgafwclass == NULL); + + fpga_data = devm_kzalloc(&pdev->dev, sizeof(struct seastone2_fpga_data), + GFP_KERNEL); + + if (!fpga_data) + return -ENOMEM; + + // Set default read address to VERSION + fpga_data->fpga_read_addr = fpga_dev.data_base_addr+FPGA_VERSION; + fpga_data->cpld1_read_addr = 0x00; + fpga_data->cpld2_read_addr = 0x00; + + mutex_init(&fpga_data->fpga_lock); + for(ret=I2C_MASTER_CH_1 ;ret <= I2C_MASTER_CH_TOTAL; ret++){ + mutex_init(&fpga_i2c_master_locks[ret-1]); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + printk(KERN_ERR "Specified Resource Not Available...\n"); + kzfree(fpga_data); + return -1; + } + + fpga = kobject_create_and_add("FPGA", &pdev->dev.kobj); + if (!fpga){ + kzfree(fpga_data); + return -ENOMEM; + } + + ret = sysfs_create_group(fpga, &fpga_attr_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create FPGA sysfs attributes\n"); + kobject_put(fpga); + kzfree(fpga_data); + return ret; + } + + cpld1 = kobject_create_and_add("CPLD1", &pdev->dev.kobj); + if (!cpld1){ + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return -ENOMEM; + } + ret = sysfs_create_group(cpld1, &cpld1_attr_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create CPLD1 sysfs attributes\n"); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return ret; + } + + cpld2 = kobject_create_and_add("CPLD2", &pdev->dev.kobj); + if (!cpld2){ + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return -ENOMEM; + } + ret = sysfs_create_group(cpld2, &cpld2_attr_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create CPLD2 sysfs attributes\n"); + kobject_put(cpld2); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return ret; + } + + sff_dev = device_create(fpgafwclass, NULL, MKDEV(0,0), NULL, "sff_device"); + if (IS_ERR(sff_dev)){ + printk(KERN_ERR "Failed to create sff device\n"); + sysfs_remove_group(cpld2, &cpld2_attr_grp); + kobject_put(cpld2); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return PTR_ERR(sff_dev); + } + + ret = sysfs_create_group(&sff_dev->kobj, &sff_led_test_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create SFF attributes\n"); + device_destroy(fpgafwclass, MKDEV(0,0)); + sysfs_remove_group(cpld2, &cpld2_attr_grp); + kobject_put(cpld2); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return ret; + } + + ret = sysfs_create_link(&pdev->dev.kobj,&sff_dev->kobj,"SFF"); + if (ret != 0){ + sysfs_remove_group(&sff_dev->kobj, &sff_led_test_grp); + device_destroy(fpgafwclass, MKDEV(0,0)); + sysfs_remove_group(cpld2, &cpld2_attr_grp); + kobject_put(cpld2); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kzfree(fpga_data); + return ret; + } + + for(portid_count=0 ; portid_count < VIRTUAL_I2C_PORT_LENGTH ; portid_count++){ + fpga_data->i2c_adapter[portid_count] = seastone2_i2c_init(pdev, portid_count, VIRTUAL_I2C_BUS_OFFSET); + } + + + /* Init SFF devices */ + for(portid_count=0; portid_count < SFF_PORT_TOTAL; portid_count++){ + struct i2c_adapter *i2c_adap = fpga_data->i2c_adapter[portid_count]; + if(i2c_adap){ + fpga_data->sff_devices[portid_count] = seastone2_sff_init(portid_count); + sff_data = dev_get_drvdata(fpga_data->sff_devices[portid_count]); + BUG_ON(sff_data == NULL); + if( sff_data->port_type == QSFP ){ + fpga_data->sff_i2c_clients[portid_count] = i2c_new_device(i2c_adap, &sff8436_eeprom_info[0]); + }else{ + fpga_data->sff_i2c_clients[portid_count] = i2c_new_device(i2c_adap, &sff8436_eeprom_info[1]); + } + sff_data = NULL; + sysfs_create_link(&fpga_data->sff_devices[portid_count]->kobj, + &fpga_data->sff_i2c_clients[portid_count]->dev.kobj, + "i2c"); + } + + } + + printk(KERN_INFO "Virtual I2C buses created\n"); + +#ifdef TEST_MODE + return 0; +#endif + fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],CPLD1_SLAVE_ADDR,0x00, + I2C_SMBUS_READ,0x00,I2C_SMBUS_BYTE_DATA,(union i2c_smbus_data*)&cpld1_version); + fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],CPLD2_SLAVE_ADDR,0x00, + I2C_SMBUS_READ,0x00,I2C_SMBUS_BYTE_DATA,(union i2c_smbus_data*)&cpld2_version); + + printk(KERN_INFO "CPLD1 VERSON: %2.2x\n", cpld1_version); + printk(KERN_INFO "CPLD2 VERSON: %2.2x\n", cpld2_version); + + /* Init I2C buses that has PCA9548 switch device. */ + for (portid_count = 0; portid_count < VIRTUAL_I2C_PORT_LENGTH; portid_count++) { + + struct i2c_dev_data *dev_data; + unsigned char master_bus; + unsigned char switch_addr; + + dev_data = i2c_get_adapdata(fpga_data->i2c_adapter[portid_count]); + master_bus = dev_data->pca9548.master_bus; + switch_addr = dev_data->pca9548.switch_addr; + + if (switch_addr != 0xFF) { + + if (prev_i2c_switch != ( (master_bus << 8) | switch_addr) ) { + // Found the bus with PCA9548, trying to clear all switch in it. + smbus_access(fpga_data->i2c_adapter[portid_count], switch_addr, 0x00, + I2C_SMBUS_WRITE, 0x00, I2C_SMBUS_BYTE, NULL); + prev_i2c_switch = ( master_bus << 8 ) | switch_addr; + } + } + } + return 0; +} + +static int seastone2_drv_remove(struct platform_device *pdev) +{ + int portid_count; + struct sff_device_data *rem_data; + + for(portid_count=0; portid_count < SFF_PORT_TOTAL; portid_count++){ + sysfs_remove_link(&fpga_data->sff_devices[portid_count]->kobj,"i2c"); + i2c_unregister_device(fpga_data->sff_i2c_clients[portid_count]); + } + + for(portid_count=0 ; portid_count < VIRTUAL_I2C_PORT_LENGTH ; portid_count++){ + if(fpga_data->i2c_adapter[portid_count] != NULL){ + info(KERN_INFO "<%x>",fpga_data->i2c_adapter[portid_count]); + i2c_del_adapter(fpga_data->i2c_adapter[portid_count]); + } + } + + for (portid_count=0; portid_count < SFF_PORT_TOTAL; portid_count++){ + if(fpga_data->sff_devices[portid_count] != NULL){ + rem_data = dev_get_drvdata(fpga_data->sff_devices[portid_count]); + device_unregister(fpga_data->sff_devices[portid_count]); + put_device(fpga_data->sff_devices[portid_count]); + kfree(rem_data); + } + } + + sysfs_remove_group(fpga, &fpga_attr_grp); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + sysfs_remove_group(cpld2, &cpld2_attr_grp); + sysfs_remove_group(&sff_dev->kobj, &sff_led_test_grp); + kobject_put(fpga); + kobject_put(cpld1); + kobject_put(cpld2); + device_destroy(fpgafwclass, MKDEV(0,0)); + devm_kfree(&pdev->dev, fpga_data); + return 0; +} + +static struct platform_driver seastone2_drv = { + .probe = seastone2_drv_probe, + .remove = __exit_p(seastone2_drv_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +#ifdef TEST_MODE + #define FPGA_PCI_BAR_NUM 2 +#else + #define FPGA_PCI_BAR_NUM 0 +#endif + +static const struct pci_device_id fpga_id_table[] = { + { PCI_VDEVICE(XILINX, FPGA_PCIE_DEVICE_ID) }, + { PCI_VDEVICE(TEST, TEST_PCIE_DEVICE_ID) }, + {0, } +}; + +MODULE_DEVICE_TABLE(pci, fpga_id_table); + +static int fpga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int err; + struct device *dev = &pdev->dev; + uint32_t fpga_version; + + if ((err = pci_enable_device(pdev))) { + dev_err(dev, "pci_enable_device probe error %d for device %s\n", + err, pci_name(pdev)); + return err; + } + + if ((err = pci_request_regions(pdev, FPGA_PCI_NAME)) < 0) { + dev_err(dev, "pci_request_regions error %d\n", err); + goto pci_disable; + } + + /* bar0: data mmio region */ + fpga_dev.data_mmio_start = pci_resource_start(pdev, FPGA_PCI_BAR_NUM); + fpga_dev.data_mmio_len = pci_resource_len(pdev, FPGA_PCI_BAR_NUM); + fpga_dev.data_base_addr = pci_iomap(pdev, FPGA_PCI_BAR_NUM, 0); + if (!fpga_dev.data_base_addr) { + dev_err(dev, "cannot iomap region of size %lu\n", + (unsigned long)fpga_dev.data_mmio_len); + goto pci_release; + } + dev_info(dev, "data_mmio iomap base = 0x%lx \n", + (unsigned long)fpga_dev.data_base_addr); + dev_info(dev, "data_mmio_start = 0x%lx data_mmio_len = %lu\n", + (unsigned long)fpga_dev.data_mmio_start, + (unsigned long)fpga_dev.data_mmio_len); + + printk(KERN_INFO "FPGA PCIe driver probe OK.\n"); + printk(KERN_INFO "FPGA ioremap registers of size %lu\n",(unsigned long)fpga_dev.data_mmio_len); + printk(KERN_INFO "FPGA Virtual BAR %d at %8.8lx - %8.8lx\n", FPGA_PCI_BAR_NUM, + (unsigned long)fpga_dev.data_base_addr, + (unsigned long)(fpga_dev.data_base_addr + fpga_dev.data_mmio_len)); + printk(KERN_INFO ""); + fpga_version = ioread32(fpga_dev.data_base_addr); + printk(KERN_INFO "FPGA VERSION : %8.8x\n", fpga_version); + fpgafw_init(); + platform_device_register(&seastone2_dev); + platform_driver_register(&seastone2_drv); + return 0; + +pci_release: + pci_release_regions(pdev); +pci_disable: + pci_disable_device(pdev); + return -EBUSY; +} + +static void fpga_pci_remove(struct pci_dev *pdev) +{ + platform_driver_unregister(&seastone2_drv); + platform_device_unregister(&seastone2_dev); + fpgafw_exit(); + pci_iounmap(pdev, fpga_dev.data_base_addr); + pci_release_regions(pdev); + pci_disable_device(pdev); + printk(KERN_INFO "FPGA PCIe driver remove OK.\n"); +}; + +static struct pci_driver pci_dev_ops = { + .name = FPGA_PCI_NAME, + .probe = fpga_pci_probe, + .remove = fpga_pci_remove, + .id_table = fpga_id_table, +}; + +enum{ + READREG, + WRITEREG +}; + +struct fpga_reg_data { + uint32_t addr; + uint32_t value; +}; + +static long fpgafw_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ + int ret = 0; + struct fpga_reg_data data; + mutex_lock(&fpga_data->fpga_lock); + +#ifdef TEST_MODE + static uint32_t status_reg; +#endif + // Switch function to read and write. + switch (cmd){ + case READREG: + if (copy_from_user(&data, (void __user*)arg, sizeof(data)) != 0){ + mutex_unlock(&fpga_data->fpga_lock); + return -EFAULT; + } + data.value = ioread32(fpga_dev.data_base_addr+data.addr); + if (copy_to_user((void __user*)arg ,&data, sizeof(data)) != 0){ + mutex_unlock(&fpga_data->fpga_lock); + return -EFAULT; + } +#ifdef TEST_MODE + if(data.addr == 0x1210){ + switch (status_reg){ + case 0x0000 : status_reg=0x8000; + break; + + case 0x8080 : status_reg=0x80C0; + break; + case 0x80C0 : status_reg=0x80F0; + break; + case 0x80F0 : status_reg=0x80F8; + break; + + } + iowrite32(status_reg,fpga_dev.data_base_addr+0x1210); + } +#endif + + + break; + case WRITEREG: + if (copy_from_user(&data, (void __user*)arg, sizeof(data)) != 0){ + mutex_unlock(&fpga_data->fpga_lock); + return -EFAULT; + } + iowrite32(data.value,fpga_dev.data_base_addr+data.addr); + +#ifdef TEST_MODE + if(data.addr == 0x1204){ + status_reg=0x8080; + iowrite32(status_reg,fpga_dev.data_base_addr+0x1210); + } +#endif + + break; + default: + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + mutex_unlock(&fpga_data->fpga_lock); + return ret; +} + + +const struct file_operations fpgafw_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = fpgafw_unlocked_ioctl, +}; + + +static int fpgafw_init(void){ + printk(KERN_INFO "Initializing the switchboard driver\n"); + // Try to dynamically allocate a major number for the device -- more difficult but worth it + majorNumber = register_chrdev(0, DEVICE_NAME, &fpgafw_fops); + if (majorNumber<0){ + printk(KERN_ALERT "Failed to register a major number\n"); + return majorNumber; + } + printk(KERN_INFO "Device registered correctly with major number %d\n", majorNumber); + + // Register the device class + fpgafwclass = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(fpgafwclass)){ // Check for error and clean up if there is + unregister_chrdev(majorNumber, DEVICE_NAME); + printk(KERN_ALERT "Failed to register device class\n"); + return PTR_ERR(fpgafwclass); // 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; + return 0; +} + +void seastone2_exit(void) +{ + pci_unregister_driver(&pci_dev_ops); +} + +module_init(seastone2_init); +module_exit(seastone2_exit); + +MODULE_AUTHOR("Pradchaya P. pphuhcar@celestica.com"); +MODULE_DESCRIPTION("Celestica seastone2 platform driver"); +MODULE_VERSION(MOD_VERSION); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/seastone2/systemd/platform-modules-seastone2.service b/platform/broadcom/sonic-platform-modules-cel/seastone2/systemd/platform-modules-seastone2.service new file mode 100644 index 0000000000..02553bf245 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/seastone2/systemd/platform-modules-seastone2.service @@ -0,0 +1,14 @@ + +[Unit] +Description=Celestica seastone2 platform modules +After=local-fs.target +Before=pmon.service + +[Service] +Type=oneshot +ExecStart=-/etc/init.d/platform-modules-seastone2 start +ExecStop=-/etc/init.d/platform-modules-seastone2 stop +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/src/sonic-device-data/tests/permitted_list b/src/sonic-device-data/tests/permitted_list index 5bddb7a0de..7740da96aa 100644 --- a/src/sonic-device-data/tests/permitted_list +++ b/src/sonic-device-data/tests/permitted_list @@ -210,3 +210,10 @@ sai_preinit_cmd_file sai_preinit_warmboot_cmd_file sai_postinit_cmd_file sai_postinit_warmboot_cmd_file +help_cli_enable +memlist_enable +serdes_lane_config_dfe +serdes_fec_enable +pbmp_gport_stack +reglist_enable +scache_filename