diff --git a/device/celestica/x86_64-cel_questone2bd-r0/Questone2bd/port_config.ini b/device/celestica/x86_64-cel_questone2bd-r0/Questone2bd/port_config.ini new file mode 100644 index 00000000000..84322530a99 --- /dev/null +++ b/device/celestica/x86_64-cel_questone2bd-r0/Questone2bd/port_config.ini @@ -0,0 +1,57 @@ +# name lanes alias fec index speed +Ethernet1 49 SFP1 rs 1 25000 +Ethernet2 50 SFP2 rs 2 25000 +Ethernet3 51 SFP3 rs 3 25000 +Ethernet4 52 SFP4 rs 4 25000 +Ethernet5 13 SFP5 rs 5 25000 +Ethernet6 14 SFP6 rs 6 25000 +Ethernet7 15 SFP7 rs 7 25000 +Ethernet8 16 SFP8 rs 8 25000 +Ethernet9 21 SFP9 rs 9 25000 +Ethernet10 22 SFP10 rs 10 25000 +Ethernet11 23 SFP11 rs 11 25000 +Ethernet12 24 SFP12 rs 12 25000 +Ethernet13 29 SFP13 rs 13 25000 +Ethernet14 30 SFP14 rs 14 25000 +Ethernet15 31 SFP15 rs 15 25000 +Ethernet16 32 SFP16 rs 16 25000 +Ethernet17 33 SFP17 rs 17 25000 +Ethernet18 34 SFP18 rs 18 25000 +Ethernet19 35 SFP19 rs 19 25000 +Ethernet20 36 SFP20 rs 20 25000 +Ethernet21 41 SFP21 rs 21 25000 +Ethernet22 42 SFP22 rs 22 25000 +Ethernet23 43 SFP23 rs 23 25000 +Ethernet24 44 SFP24 rs 24 25000 +Ethernet25 1 SFP25 rs 25 25000 +Ethernet26 2 SFP26 rs 26 25000 +Ethernet27 3 SFP27 rs 27 25000 +Ethernet28 4 SFP28 rs 28 25000 +Ethernet29 5 SFP29 rs 29 25000 +Ethernet30 6 SFP30 rs 30 25000 +Ethernet31 7 SFP31 rs 31 25000 +Ethernet32 8 SFP32 rs 32 25000 +Ethernet33 85 SFP33 rs 33 25000 +Ethernet34 86 SFP34 rs 34 25000 +Ethernet35 87 SFP35 rs 35 25000 +Ethernet36 88 SFP36 rs 36 25000 +Ethernet37 93 SFP37 rs 37 25000 +Ethernet38 94 SFP38 rs 38 25000 +Ethernet39 95 SFP39 rs 39 25000 +Ethernet40 96 SFP40 rs 40 25000 +Ethernet41 97 SFP41 rs 41 25000 +Ethernet42 98 SFP42 rs 42 25000 +Ethernet43 99 SFP43 rs 43 25000 +Ethernet44 100 SFP44 rs 44 25000 +Ethernet45 105 SFP45 rs 45 25000 +Ethernet46 106 SFP46 rs 46 25000 +Ethernet47 107 SFP47 rs 47 25000 +Ethernet48 108 SFP48 rs 48 25000 +Ethernet49 57,58,59,60 QSFP1 rs 49 100000 +Ethernet50 61,62,63,64 QSFP2 rs 50 100000 +Ethernet51 121,122,123,124 QSFP3 rs 51 100000 +Ethernet52 113,114,115,116 QSFP4 rs 52 100000 +Ethernet53 125,126,127,128 QSFP5 rs 53 100000 +Ethernet54 65,66,67,68 QSFP6 rs 54 100000 +Ethernet55 77,78,79,80 QSFP7 rs 55 100000 +Ethernet56 69,70,71,72 QSFP8 rs 56 100000 \ No newline at end of file diff --git a/device/celestica/x86_64-cel_questone2bd-r0/Questone2bd/sai.profile b/device/celestica/x86_64-cel_questone2bd-r0/Questone2bd/sai.profile new file mode 100644 index 00000000000..0be570cdb38 --- /dev/null +++ b/device/celestica/x86_64-cel_questone2bd-r0/Questone2bd/sai.profile @@ -0,0 +1 @@ +SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/td3-questone2bd.config.bcm diff --git a/device/celestica/x86_64-cel_questone2bd-r0/Questone2bd/td3-questone2bd.config.bcm b/device/celestica/x86_64-cel_questone2bd-r0/Questone2bd/td3-questone2bd.config.bcm new file mode 100644 index 00000000000..eac64cd69b7 --- /dev/null +++ b/device/celestica/x86_64-cel_questone2bd-r0/Questone2bd/td3-questone2bd.config.bcm @@ -0,0 +1,406 @@ +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_xe=13 +serdes_if_type_ce=14 +pbmp_gport_stack.0=0x0000000000000000000000000000000000000000000000000000000000000000 +#pbmp_xport_xe=0x48878787f8787808dfe1e0203e1e1e022 +pbmp_xport_xe=0x48808787f8780808c221e1e1fe1e1e1fe + + +portmap_1.0=1:25 +portmap_2.0=2:25 +portmap_3.0=3:25 +portmap_4.0=4:25 +portmap_5.0=5:25 +portmap_6.0=6:25 +portmap_7.0=7:25 +portmap_8.0=8:25 +portmap_13.0=13:25 +portmap_14.0=14:25 +portmap_15.0=15:25 +portmap_16.0=16:25 +portmap_21.0=21:25 +portmap_22.0=22:25 +portmap_23.0=23:25 +portmap_24.0=24:25 +portmap_29.0=29:25 +portmap_30.0=30:25 +portmap_31.0=31:25 +portmap_32.0=32:25 +portmap_33.0=33:25 +portmap_34.0=34:25 +portmap_35.0=35:25 +portmap_36.0=36:25 +portmap_41.0=41:25 +portmap_42.0=42:25 +portmap_43.0=43:25 +portmap_44.0=44:25 +portmap_49.0=49:25 +portmap_50.0=50:25 +portmap_51.0=51:25 +portmap_52.0=52:25 +portmap_57.0=57:100 +portmap_61.0=61:100 +portmap_67.0=65:100 +portmap_71.0=69:100 +portmap_79.0=77:100 +portmap_87.0=85:25 +portmap_88.0=86:25 +portmap_89.0=87:25 +portmap_90.0=88:25 +portmap_95.0=93:25 +portmap_96.0=94:25 +portmap_97.0=95:25 +portmap_98.0=96:25 +portmap_99.0=97:25 +portmap_100.0=98:25 +portmap_101.0=99:25 +portmap_102.0=100:25 +portmap_107.0=105:25 +portmap_108.0=106:25 +portmap_109.0=107:25 +portmap_110.0=108:25 +portmap_115.0=113:100 +portmap_123.0=121:100 +portmap_127.0=125:100 + +phy_chain_tx_lane_map_physical{1.0}=0x0123 +phy_chain_rx_lane_map_physical{1.0}=0x1032 +phy_chain_tx_lane_map_physical{5.0}=0x0123 +phy_chain_rx_lane_map_physical{5.0}=0x1032 +phy_chain_tx_lane_map_physical{13.0}=0x0123 +phy_chain_rx_lane_map_physical{13.0}=0x1032 +phy_chain_tx_lane_map_physical{21.0}=0x0123 +phy_chain_rx_lane_map_physical{21.0}=0x1032 +phy_chain_tx_lane_map_physical{29.0}=0x0123 +phy_chain_rx_lane_map_physical{29.0}=0x1032 +phy_chain_tx_lane_map_physical{33.0}=0x0123 +phy_chain_rx_lane_map_physical{33.0}=0x1032 +phy_chain_tx_lane_map_physical{41.0}=0x0123 +phy_chain_rx_lane_map_physical{41.0}=0x1032 +phy_chain_tx_lane_map_physical{49.0}=0x0123 +phy_chain_rx_lane_map_physical{49.0}=0x1032 +phy_chain_tx_lane_map_physical{57.0}=0x2031 +phy_chain_rx_lane_map_physical{57.0}=0x1023 +phy_chain_tx_lane_map_physical{61.0}=0x1302 +phy_chain_rx_lane_map_physical{61.0}=0x0123 +phy_chain_tx_lane_map_physical{65.0}=0x3210 +phy_chain_rx_lane_map_physical{65.0}=0x3210 +phy_chain_tx_lane_map_physical{69.0}=0x0123 +phy_chain_rx_lane_map_physical{69.0}=0x3120 +phy_chain_tx_lane_map_physical{77.0}=0x1302 +phy_chain_rx_lane_map_physical{77.0}=0x2031 +phy_chain_tx_lane_map_physical{85.0}=0x3210 +phy_chain_rx_lane_map_physical{85.0}=0x2301 +phy_chain_tx_lane_map_physical{93.0}=0x3210 +phy_chain_rx_lane_map_physical{93.0}=0x2301 +phy_chain_tx_lane_map_physical{97.0}=0x3210 +phy_chain_rx_lane_map_physical{97.0}=0x2301 +phy_chain_tx_lane_map_physical{105.0}=0x3210 +phy_chain_rx_lane_map_physical{105.0}=0x2301 +phy_chain_tx_lane_map_physical{113.0}=0x0132 +phy_chain_rx_lane_map_physical{113.0}=0x3120 +phy_chain_tx_lane_map_physical{121.0}=0x2301 +phy_chain_rx_lane_map_physical{121.0}=0x1023 +phy_chain_tx_lane_map_physical{125.0}=0x3210 +phy_chain_rx_lane_map_physical{125.0}=0x2031 +phy_chain_tx_polarity_flip_physical{1.0}=0x0 +phy_chain_rx_polarity_flip_physical{1.0}=0x1 +phy_chain_tx_polarity_flip_physical{2.0}=0x1 +phy_chain_rx_polarity_flip_physical{2.0}=0x0 +phy_chain_tx_polarity_flip_physical{3.0}=0x0 +phy_chain_rx_polarity_flip_physical{3.0}=0x1 +phy_chain_tx_polarity_flip_physical{4.0}=0x1 +phy_chain_rx_polarity_flip_physical{4.0}=0x0 +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}=0x0 +phy_chain_tx_polarity_flip_physical{8.0}=0x1 +phy_chain_rx_polarity_flip_physical{8.0}=0x1 +phy_chain_tx_polarity_flip_physical{13.0}=0x0 +phy_chain_rx_polarity_flip_physical{13.0}=0x1 +phy_chain_tx_polarity_flip_physical{14.0}=0x1 +phy_chain_rx_polarity_flip_physical{14.0}=0x0 +phy_chain_tx_polarity_flip_physical{15.0}=0x0 +phy_chain_rx_polarity_flip_physical{15.0}=0x1 +phy_chain_tx_polarity_flip_physical{16.0}=0x1 +phy_chain_rx_polarity_flip_physical{16.0}=0x0 +phy_chain_tx_polarity_flip_physical{21.0}=0x0 +phy_chain_rx_polarity_flip_physical{21.0}=0x1 +phy_chain_tx_polarity_flip_physical{22.0}=0x1 +phy_chain_rx_polarity_flip_physical{22.0}=0x0 +phy_chain_tx_polarity_flip_physical{23.0}=0x0 +phy_chain_rx_polarity_flip_physical{23.0}=0x1 +phy_chain_tx_polarity_flip_physical{24.0}=0x1 +phy_chain_rx_polarity_flip_physical{24.0}=0x0 +phy_chain_tx_polarity_flip_physical{29.0}=0x0 +phy_chain_rx_polarity_flip_physical{29.0}=0x0 +phy_chain_tx_polarity_flip_physical{30.0}=0x1 +phy_chain_rx_polarity_flip_physical{30.0}=0x1 +phy_chain_tx_polarity_flip_physical{31.0}=0x0 +phy_chain_rx_polarity_flip_physical{31.0}=0x0 +phy_chain_tx_polarity_flip_physical{32.0}=0x1 +phy_chain_rx_polarity_flip_physical{32.0}=0x1 +phy_chain_tx_polarity_flip_physical{33.0}=0x0 +phy_chain_rx_polarity_flip_physical{33.0}=0x1 +phy_chain_tx_polarity_flip_physical{34.0}=0x1 +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}=0x1 +phy_chain_tx_polarity_flip_physical{36.0}=0x1 +phy_chain_rx_polarity_flip_physical{36.0}=0x0 +phy_chain_tx_polarity_flip_physical{41.0}=0x0 +phy_chain_rx_polarity_flip_physical{41.0}=0x1 +phy_chain_tx_polarity_flip_physical{42.0}=0x1 +phy_chain_rx_polarity_flip_physical{42.0}=0x0 +phy_chain_tx_polarity_flip_physical{43.0}=0x0 +phy_chain_rx_polarity_flip_physical{43.0}=0x1 +phy_chain_tx_polarity_flip_physical{44.0}=0x1 +phy_chain_rx_polarity_flip_physical{44.0}=0x0 +phy_chain_tx_polarity_flip_physical{49.0}=0x0 +phy_chain_rx_polarity_flip_physical{49.0}=0x0 +phy_chain_tx_polarity_flip_physical{50.0}=0x0 +phy_chain_rx_polarity_flip_physical{50.0}=0x0 +phy_chain_tx_polarity_flip_physical{51.0}=0x1 +phy_chain_rx_polarity_flip_physical{51.0}=0x0 +phy_chain_tx_polarity_flip_physical{52.0}=0x0 +phy_chain_rx_polarity_flip_physical{52.0}=0x0 +phy_chain_tx_polarity_flip_physical{57.0}=0x0 +phy_chain_rx_polarity_flip_physical{57.0}=0x1 +phy_chain_tx_polarity_flip_physical{58.0}=0x0 +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 +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}=0x0 +phy_chain_rx_polarity_flip_physical{62.0}=0x1 +phy_chain_tx_polarity_flip_physical{63.0}=0x1 +phy_chain_rx_polarity_flip_physical{63.0}=0x1 +phy_chain_tx_polarity_flip_physical{64.0}=0x1 +phy_chain_rx_polarity_flip_physical{64.0}=0x1 +phy_chain_tx_polarity_flip_physical{65.0}=0x1 +phy_chain_rx_polarity_flip_physical{65.0}=0x1 +phy_chain_tx_polarity_flip_physical{66.0}=0x1 +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}=0x1 +phy_chain_tx_polarity_flip_physical{69.0}=0x0 +phy_chain_rx_polarity_flip_physical{69.0}=0x1 +phy_chain_tx_polarity_flip_physical{70.0}=0x0 +phy_chain_rx_polarity_flip_physical{70.0}=0x1 +phy_chain_tx_polarity_flip_physical{71.0}=0x1 +phy_chain_rx_polarity_flip_physical{71.0}=0x0 +phy_chain_tx_polarity_flip_physical{72.0}=0x0 +phy_chain_rx_polarity_flip_physical{72.0}=0x1 +phy_chain_tx_polarity_flip_physical{77.0}=0x0 +phy_chain_rx_polarity_flip_physical{77.0}=0x1 +phy_chain_tx_polarity_flip_physical{78.0}=0x0 +phy_chain_rx_polarity_flip_physical{78.0}=0x0 +phy_chain_tx_polarity_flip_physical{79.0}=0x0 +phy_chain_rx_polarity_flip_physical{79.0}=0x0 +phy_chain_tx_polarity_flip_physical{80.0}=0x1 +phy_chain_rx_polarity_flip_physical{80.0}=0x0 +phy_chain_tx_polarity_flip_physical{85.0}=0x1 +phy_chain_rx_polarity_flip_physical{85.0}=0x0 +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}=0x1 +phy_chain_tx_polarity_flip_physical{93.0}=0x1 +phy_chain_rx_polarity_flip_physical{93.0}=0x0 +phy_chain_tx_polarity_flip_physical{94.0}=0x0 +phy_chain_rx_polarity_flip_physical{94.0}=0x1 +phy_chain_tx_polarity_flip_physical{95.0}=0x1 +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 +phy_chain_tx_polarity_flip_physical{97.0}=0x1 +phy_chain_rx_polarity_flip_physical{97.0}=0x1 +phy_chain_tx_polarity_flip_physical{98.0}=0x0 +phy_chain_rx_polarity_flip_physical{98.0}=0x0 +phy_chain_tx_polarity_flip_physical{99.0}=0x1 +phy_chain_rx_polarity_flip_physical{99.0}=0x1 +phy_chain_tx_polarity_flip_physical{100.0}=0x0 +phy_chain_rx_polarity_flip_physical{100.0}=0x0 +phy_chain_tx_polarity_flip_physical{105.0}=0x1 +phy_chain_rx_polarity_flip_physical{105.0}=0x0 +phy_chain_tx_polarity_flip_physical{106.0}=0x0 +phy_chain_rx_polarity_flip_physical{106.0}=0x1 +phy_chain_tx_polarity_flip_physical{107.0}=0x1 +phy_chain_rx_polarity_flip_physical{107.0}=0x0 +phy_chain_tx_polarity_flip_physical{108.0}=0x0 +phy_chain_rx_polarity_flip_physical{108.0}=0x1 +phy_chain_tx_polarity_flip_physical{113.0}=0x1 +phy_chain_rx_polarity_flip_physical{113.0}=0x1 +phy_chain_tx_polarity_flip_physical{114.0}=0x1 +phy_chain_rx_polarity_flip_physical{114.0}=0x1 +phy_chain_tx_polarity_flip_physical{115.0}=0x0 +phy_chain_rx_polarity_flip_physical{115.0}=0x0 +phy_chain_tx_polarity_flip_physical{116.0}=0x0 +phy_chain_rx_polarity_flip_physical{116.0}=0x1 +phy_chain_tx_polarity_flip_physical{121.0}=0x1 +phy_chain_rx_polarity_flip_physical{121.0}=0x1 +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}=0x0 +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 +phy_chain_tx_polarity_flip_physical{125.0}=0x0 +phy_chain_rx_polarity_flip_physical{125.0}=0x0 +phy_chain_tx_polarity_flip_physical{126.0}=0x0 +phy_chain_rx_polarity_flip_physical{126.0}=0x1 +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}=0x1 + + +dport_map_port_49=1 +dport_map_port_50=2 +dport_map_port_51=3 +dport_map_port_52=4 +dport_map_port_13=5 +dport_map_port_14=6 +dport_map_port_15=7 +dport_map_port_16=8 +dport_map_port_21=9 +dport_map_port_22=10 +dport_map_port_23=11 +dport_map_port_24=12 +dport_map_port_29=13 +dport_map_port_30=14 +dport_map_port_31=15 +dport_map_port_32=16 +dport_map_port_33=17 +dport_map_port_34=18 +dport_map_port_35=19 +dport_map_port_36=20 +dport_map_port_41=21 +dport_map_port_42=22 +dport_map_port_43=23 +dport_map_port_44=24 +dport_map_port_1=25 +dport_map_port_2=26 +dport_map_port_3=27 +dport_map_port_4=28 +dport_map_port_5=29 +dport_map_port_6=30 +dport_map_port_7=31 +dport_map_port_8=32 +dport_map_port_87=33 +dport_map_port_88=34 +dport_map_port_89=35 +dport_map_port_90=36 +dport_map_port_95=37 +dport_map_port_96=38 +dport_map_port_97=39 +dport_map_port_98=40 +dport_map_port_99=41 +dport_map_port_100=42 +dport_map_port_101=43 +dport_map_port_102=44 +dport_map_port_107=45 +dport_map_port_108=46 +dport_map_port_109=47 +dport_map_port_110=48 +dport_map_port_57=49 +dport_map_port_61=50 +dport_map_port_123=51 +dport_map_port_115=52 +dport_map_port_127=53 +dport_map_port_67=54 +dport_map_port_79=55 +dport_map_port_71=56 + +#Pre-emphasis configuration for optical module +# configuration for 25G optical module +serdes_preemphasis_49=0x134809 +serdes_preemphasis_50=0x124909 +serdes_preemphasis_51=0x124909 +serdes_preemphasis_52=0x124909 +serdes_preemphasis_13=0x124909 +serdes_preemphasis_14=0x124909 +serdes_preemphasis_15=0x124909 +serdes_preemphasis_16=0x124909 +serdes_preemphasis_21=0x104b09 +serdes_preemphasis_22=0x104b09 +serdes_preemphasis_23=0x0f4c09 +serdes_preemphasis_24=0x0f4c09 +serdes_preemphasis_29=0x0f4c09 +serdes_preemphasis_30=0x0f4c09 +serdes_preemphasis_31=0x0f4c09 +serdes_preemphasis_32=0x0e4d09 +serdes_preemphasis_33=0x0e4d09 +serdes_preemphasis_34=0x0c4f09 +serdes_preemphasis_35=0x0c4f09 +serdes_preemphasis_36=0x0c4f09 +serdes_preemphasis_41=0x0c4f09 +serdes_preemphasis_42=0x0c4f09 +serdes_preemphasis_43=0x0c4f09 +serdes_preemphasis_44=0x0c4f09 +serdes_preemphasis_1=0x0f4c09 +serdes_preemphasis_2=0x0f4c09 +serdes_preemphasis_3=0x0e4d09 +serdes_preemphasis_4=0x0f4c09 +serdes_preemphasis_5=0x0e4d09 +serdes_preemphasis_6=0x0e4d09 +serdes_preemphasis_7=0x0e4d09 +serdes_preemphasis_8=0x0e4d09 +serdes_preemphasis_87=0x0c4f09 +serdes_preemphasis_88=0x0c4f09 +serdes_preemphasis_89=0x0c4f09 +serdes_preemphasis_90=0x0c4f09 +serdes_preemphasis_95=0x0c4f09 +serdes_preemphasis_96=0x0c4f09 +serdes_preemphasis_97=0x0c4f09 +serdes_preemphasis_98=0x0c4f09 +serdes_preemphasis_99=0x0c4f09 +serdes_preemphasis_100=0x0e4d09 +serdes_preemphasis_101=0x0e4d09 +serdes_preemphasis_102=0x0f4c09 +serdes_preemphasis_107=0x0f4c09 +serdes_preemphasis_108=0x0f4c09 +serdes_preemphasis_109=0x0f4c09 +serdes_preemphasis_110=0x0f4c09 + +# configuration for 100G optical module +serdes_preemphasis_57=0x154708 +serdes_preemphasis_61=0x154708 +serdes_preemphasis_123=0x154708 +serdes_preemphasis_115=0x154708 +serdes_preemphasis_127=0x154708 +serdes_preemphasis_67=0x154708 +serdes_preemphasis_79=0x154708 +serdes_preemphasis_71=0x154708 + +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_questone2bd-r0/custom_led.bin b/device/celestica/x86_64-cel_questone2bd-r0/custom_led.bin new file mode 100644 index 00000000000..b2ed8cb8d09 Binary files /dev/null and b/device/celestica/x86_64-cel_questone2bd-r0/custom_led.bin differ diff --git a/device/celestica/x86_64-cel_questone2bd-r0/default_sku b/device/celestica/x86_64-cel_questone2bd-r0/default_sku new file mode 100644 index 00000000000..5cc4302d7a0 --- /dev/null +++ b/device/celestica/x86_64-cel_questone2bd-r0/default_sku @@ -0,0 +1 @@ +Questone2bd t1 diff --git a/device/celestica/x86_64-cel_questone2bd-r0/installer.conf b/device/celestica/x86_64-cel_questone2bd-r0/installer.conf new file mode 100644 index 00000000000..dc3cf67d19e --- /dev/null +++ b/device/celestica/x86_64-cel_questone2bd-r0/installer.conf @@ -0,0 +1,2 @@ +CONSOLE_SPEED=9600 +ONIE_PLATFORM_EXTRA_CMDLINE_LINUX="processor.max_cstate=1 intel_idle.max_cstate=0" diff --git a/device/celestica/x86_64-cel_questone2bd-r0/led_proc_init.soc b/device/celestica/x86_64-cel_questone2bd-r0/led_proc_init.soc new file mode 100644 index 00000000000..ba9b419b4e9 --- /dev/null +++ b/device/celestica/x86_64-cel_questone2bd-r0/led_proc_init.soc @@ -0,0 +1,20 @@ +#Enable all ports +#port all en=1 +#sleep 6 +#linkscan 250000; port xe,ce linkscan=on +linkscan off + +m0 load 0 0x3800 /usr/share/sonic/platform/custom_led.bin +led start +linkscan on +#Load LED +#sleep 10 +#vlan clear +#sleep 5 +#vlan add 1 pbm=xe,cpu ubm=xe,cpu +#sleep 5 +#led auto on; led start +#sleep 5 +#port xe48,xe49,ce en=1 +#port xe48,xe49 an=1 +#vlan create 250 pbm=xe48,xe49 ubm=xe48,xe49 diff --git a/device/celestica/x86_64-cel_questone2bd-r0/linkscan_led_fw.bin b/device/celestica/x86_64-cel_questone2bd-r0/linkscan_led_fw.bin new file mode 100644 index 00000000000..a6a4794ecc2 Binary files /dev/null and b/device/celestica/x86_64-cel_questone2bd-r0/linkscan_led_fw.bin differ diff --git a/device/celestica/x86_64-cel_questone2bd-r0/plugins/eeprom.py b/device/celestica/x86_64-cel_questone2bd-r0/plugins/eeprom.py new file mode 100644 index 00000000000..b75d40ae12e --- /dev/null +++ b/device/celestica/x86_64-cel_questone2bd-r0/plugins/eeprom.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# +# 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 +# + +__version__ = "0.0.1" + +try: + from sonic_eeprom import eeprom_tlvinfo +except ImportError, e: + raise ImportError(str(e) + "- required module not found") + +TLV_I2C = "i2c-0" +TLV_REG = "0-0056" +TLV_PATH = "/sys/class/i2c-adapter/{}/{}/eeprom".format(TLV_I2C, TLV_REG) + + +class board(eeprom_tlvinfo.TlvInfoDecoder): + + def __init__(self, name, path, cpld_root, ro): + self.eeprom_path = TLV_PATH + super(board, self).__init__(self.eeprom_path, 0, '', True) diff --git a/device/celestica/x86_64-cel_questone2bd-r0/plugins/psuutil.py b/device/celestica/x86_64-cel_questone2bd-r0/plugins/psuutil.py new file mode 100644 index 00000000000..2f84adc9d72 --- /dev/null +++ b/device/celestica/x86_64-cel_questone2bd-r0/plugins/psuutil.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# +# Platform-specific PSU control functionality for SONiC +# + +__version__ = "0.0.1" + +try: + import os.path + import subprocess + from sonic_psu.psu_base import PsuBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +NUM_PSU = 2 +PSU_STATUS_REGISTER = "0xA160" +BASE_CPLD_PLATFORM = "questone2bd.cpldb" +GETREG_PATH = "/sys/devices/platform/{}/getreg".format(BASE_CPLD_PLATFORM) + +SCALE = 16 +BIN_BITS = 8 +PRESENT_BIT = '0' +POWER_OK_BIT = '1' +PSU_STATUS_REG_MAP = { + 1: { + "status": 0, + "present": 2 + }, + 2: { + "status": 1, + "present": 3 + }, +} + + +class PsuUtil(PsuBase): + """Platform-specific PSUutil class""" + + def __init__(self): + PsuBase.__init__(self) + self.__get_psu_status() + + def __get_register_value(self, register): + cmd = "echo {1} > {0}; cat {0}".format(GETREG_PATH, register) + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + return raw_data.strip() if not err else None + + def __hex_to_bin(self, ini_string): + return bin(int(ini_string, SCALE)).zfill(BIN_BITS) + + def __get_psu_status(self): + psu_status_raw = self.__get_register_value(PSU_STATUS_REGISTER) + psu_status_bin = self.__hex_to_bin(psu_status_raw) + psu_status_all = str(psu_status_bin)[2:][::-1] + + psu_status_dict = dict() + for psu_num in range(1, NUM_PSU+1): + psu_status = dict() + psu_status["status"] = True if psu_status_all[PSU_STATUS_REG_MAP[psu_num] + ["status"]] == POWER_OK_BIT else False + psu_status["present"] = True if psu_status_all[PSU_STATUS_REG_MAP[psu_num] + ["present"]] == PRESENT_BIT else False + psu_status_dict[psu_num] = psu_status + return psu_status_dict + + 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 NUM_PSU + + 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 + """ + psu_status_dict = self.__get_psu_status() + + return psu_status_dict[index].get("status", False) + + 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 + """ + psu_status_dict = self.__get_psu_status() + + return psu_status_dict[index].get("present", False) diff --git a/device/celestica/x86_64-cel_questone2bd-r0/plugins/sfputil.py b/device/celestica/x86_64-cel_questone2bd-r0/plugins/sfputil.py new file mode 100755 index 00000000000..73125d8ad9e --- /dev/null +++ b/device/celestica/x86_64-cel_questone2bd-r0/plugins/sfputil.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python +# +# Platform-specific SFP transceiver interface for SONiC +# + +__version__ = "0.0.1" + +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 = 56 + QSFP_PORT_START = 49 + QSFP_PORT_END = 56 + + EEPROM_OFFSET = 9 + PORT_INFO_PATH = '/sys/class/questone2bd_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): + return NotImplementedError + + 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 + """ + raise NotImplementedError + + def tx_disable(self, port_num, disable): + """ + @param port_num index of physical port + @param disable, True -- disable port tx signal + False -- enable port tx signal + @return True when operation success, False on failure. + """ + TX_DISABLE_BYTE_OFFSET = 86 + if port_num not in range(self.port_start, self.port_end + 1) or type(disable) != bool: + return False + + # QSFP, set eeprom to disable tx + if port_num in self.qsfp_ports: + presence = self.get_presence(port_num) + if not presence: + return True + + disable = b'\x0f' if disable else b'\x00' + # open eeprom + try: + with open(self.port_to_eeprom_mapping[port_num], mode="wb", buffering=0) as sysfsfile: + sysfsfile.seek(TX_DISABLE_BYTE_OFFSET) + sysfsfile.write(bytearray(disable)) + except IOError: + return False + except: + return False + + # SFP, set tx_disable pin + else: + try: + disable = hex(1) if disable else hex(0) + port_name = self.get_port_name(port_num) + reg_file = open( + "/".join([self.PORT_INFO_PATH, port_name, "sfp_txdisable"]), "w") + reg_file.write(disable) + reg_file.close() + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + return True diff --git a/device/celestica/x86_64-cel_questone2bd-r0/pmon_daemon_control.json b/device/celestica/x86_64-cel_questone2bd-r0/pmon_daemon_control.json new file mode 100644 index 00000000000..94592fa8ceb --- /dev/null +++ b/device/celestica/x86_64-cel_questone2bd-r0/pmon_daemon_control.json @@ -0,0 +1,3 @@ +{ + "skip_ledd": true +} diff --git a/platform/broadcom/one-image.mk b/platform/broadcom/one-image.mk index 8cbf7269592..0c04321c258 100644 --- a/platform/broadcom/one-image.mk +++ b/platform/broadcom/one-image.mk @@ -40,6 +40,8 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \ $(INVENTEC_D6356_PLATFORM_MODULE) \ $(CEL_DX010_PLATFORM_MODULE) \ $(CEL_HALIBURTON_PLATFORM_MODULE) \ + $(CEL_SILVERSTONE_PLATFORM_MODULE) \ + $(CEL_QUESTONE2BD_PLATFORM_MODULE) \ $(DELTA_AG9032V1_PLATFORM_MODULE) \ $(DELTA_AG9064_PLATFORM_MODULE) \ $(DELTA_AG5648_PLATFORM_MODULE) \ @@ -54,8 +56,7 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \ $(ALPHANETWORKS_SNH60B0_640F_PLATFORM_MODULE) \ $(BRCM_XLR_GTS_PLATFORM_MODULE) \ $(DELTA_AG9032V2A_PLATFORM_MODULE) \ - $(JUNIPER_QFX5210_PLATFORM_MODULE) \ - $(CEL_SILVERSTONE_PLATFORM_MODULE) + $(JUNIPER_QFX5210_PLATFORM_MODULE) ifeq ($(INSTALL_DEBUG_TOOLS),y) $(SONIC_ONE_IMAGE)_DOCKERS += $(SONIC_INSTALL_DOCKER_DBG_IMAGES) $(SONIC_ONE_IMAGE)_DOCKERS += $(filter-out $(patsubst %-$(DBG_IMAGE_MARK).gz,%.gz, $(SONIC_INSTALL_DOCKER_DBG_IMAGES)), $(SONIC_INSTALL_DOCKER_IMAGES)) diff --git a/platform/broadcom/platform-modules-cel.mk b/platform/broadcom/platform-modules-cel.mk index b7371e3282d..f2da59620a7 100644 --- a/platform/broadcom/platform-modules-cel.mk +++ b/platform/broadcom/platform-modules-cel.mk @@ -1,12 +1,14 @@ -# Celestica DX010 and Haliburton Platform modules +# Celestica platform modules -CEL_DX010_PLATFORM_MODULE_VERSION = 0.9 -CEL_HALIBURTON_PLATFORM_MODULE_VERSION = 0.9 -CEL_SILVERSTONE_PLATFORM_MODULE_VERSION = 0.9 +CEL_DX010_PLATFORM_MODULE_VERSION = 1.0 +CEL_HALIBURTON_PLATFORM_MODULE_VERSION = 1.0 +CEL_SILVERSTONE_PLATFORM_MODULE_VERSION = 1.0 +CEL_QUESTONE2BD_PLATFORM_MODULE_VERSION = 1.0 export CEL_DX010_PLATFORM_MODULE_VERSION export CEL_HALIBURTON_PLATFORM_MODULE_VERSION export CEL_SILVERSTONE_PLATFORM_MODULE_VERSION +export CEL_QUESTONE2BD_PLATFORM_MODULE_VERSION CEL_DX010_PLATFORM_MODULE = platform-modules-dx010_$(CEL_DX010_PLATFORM_MODULE_VERSION)_amd64.deb $(CEL_DX010_PLATFORM_MODULE)_SRC_PATH = $(PLATFORM_PATH)/sonic-platform-modules-cel @@ -22,4 +24,8 @@ CEL_SILVERSTONE_PLATFORM_MODULE = platform-modules-silverstone_$(CEL_SILVERSTONE $(CEL_SILVERSTONE_PLATFORM_MODULE)_PLATFORM = x86_64-cel_silverstone-r0 $(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_SILVERSTONE_PLATFORM_MODULE))) +CEL_QUESTONE2BD_PLATFORM_MODULE = platform-modules-questone2bd_$(CEL_QUESTONE2BD_PLATFORM_MODULE_VERSION)_amd64.deb +$(CEL_QUESTONE2BD_PLATFORM_MODULE)_PLATFORM = x86_64-cel_questone2bd-r0 +$(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_QUESTONE2BD_PLATFORM_MODULE))) + SONIC_STRETCH_DEBS += $(CEL_DX010_PLATFORM_MODULE) diff --git a/platform/broadcom/sonic-platform-modules-cel/README.md b/platform/broadcom/sonic-platform-modules-cel/README.md index 26e45c685e4..7365b709a95 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 products for the SONiC project diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/changelog b/platform/broadcom/sonic-platform-modules-cel/debian/changelog index 843b028e430..96efa8ed252 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/changelog +++ b/platform/broadcom/sonic-platform-modules-cel/debian/changelog @@ -1,3 +1,10 @@ +sonic-cel-platform-modules (1.0) unstable; urgency=low + + * Add questone2bd platform. + * Upgrade linux kernel to version 4.9 + + -- Wirut Getbamrung Mon, 28 Jan 2019 10:45:15 +0700 + sonic-cel-platform-modules (0.9) unstable; urgency=low * Add haliburton platform module. diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/control b/platform/broadcom/sonic-platform-modules-cel/debian/control index 2e9b578872f..2ea3c75fa0b 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/control +++ b/platform/broadcom/sonic-platform-modules-cel/debian/control @@ -1,7 +1,7 @@ Source: sonic-cel-platform-modules Section: main Priority: extra -Maintainer: Abhisit Sangjan +Maintainer: Wirut Getbamrung Build-Depends: debhelper (>= 8.0.0), bzip2 Standards-Version: 3.9.3 @@ -10,7 +10,6 @@ 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-haliburton Architecture: amd64 Depends: linux-image-4.9.0-9-2-amd64 @@ -20,3 +19,8 @@ Package: platform-modules-silverstone Architecture: amd64 Depends: linux-image-4.9.0-9-2-amd64 Description: kernel modules for platform devices such as led, sfp. + +Package: platform-modules-questone2bd +Architecture: amd64 +Depends: linux-image-4.9.0-9-2-amd64 +Description: kernel modules for platform devices such as fan, led, sfp diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2bd.init b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2bd.init new file mode 100644 index 00000000000..b3348b2ad11 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2bd.init @@ -0,0 +1,87 @@ +#!/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 questone2bd board. +### END INIT INFO + +case "$1" in +start) + echo -n "Setting up board... " + + # Loads kernel modules + modprobe i2c-dev + modprobe dimm-bus + modprobe pmbus_core + modprobe i2c-imc allow_unsafe_access=1 + modprobe baseboard_cpld + modprobe switchboard_fpga allow_unsafe_i2c_access=1 + modprobe mc24lc64t + modprobe i2c_dev_sysfs + modprobe syscpld + modprobe fancpld + modprobe dps1100 + + # Add driver to support TLV - EEPROM + echo 24lc64t 0x56 > /sys/bus/i2c/devices/i2c-0/new_device + + # Init the PCA9548 on FAN, PSU, and Power busses. + echo pca9548 0x77 > /sys/bus/i2c/devices/i2c-66/new_device + echo pca9548 0x77 > /sys/bus/i2c/devices/i2c-68/new_device + echo pca9548 0x77 > /sys/bus/i2c/devices/i2c-71/new_device + + ## Populate FAN CPLD device + echo fancpld 0x0d > /sys/bus/i2c/devices/i2c-66/new_device + + ## Populate SYSTEM cpld device + echo syscpld 0x0d > /sys/bus/i2c/devices/i2c-70/new_device + + ## Populate PSU PMBus and System CPLD devices + sleep 1 + echo dps1100 0x58 > /sys/bus/i2c/devices/i2c-75/new_device + sleep 1 + echo dps1100 0x59 > /sys/bus/i2c/devices/i2c-76/new_device + + # Populate temperature sensors + echo lm75b 0x48 > /sys/bus/i2c/devices/i2c-9/new_device + echo lm75b 0x4d > /sys/bus/i2c/devices/i2c-67/new_device + + # Populate EEPROM + ## FAN + echo 24lc64t 0x50 > /sys/bus/i2c/devices/i2c-2/new_device + echo 24lc64t 0x50 > /sys/bus/i2c/devices/i2c-4/new_device + echo 24lc64t 0x50 > /sys/bus/i2c/devices/i2c-6/new_device + echo 24lc64t 0x50 > /sys/bus/i2c/devices/i2c-8/new_device + echo 24lc64t 0x56 > /sys/bus/i2c/devices/i2c-9/new_device + + ## PSU + echo 24lc64t 0x50 > /sys/bus/i2c/devices/i2c-75/new_device + echo 24lc64t 0x51 > /sys/bus/i2c/devices/i2c-76/new_device + + ## SYSTEM + echo 24lc64t 0x57 > /sys/bus/i2c/devices/i2c-69/new_device + + echo "done." + ;; + +stop) + echo "done." + ;; + +force-reload|restart) + echo "Not supported" + ;; + +*) + echo "Usage: /etc/init.d/platform-modules-questone2bd.init {start|stop}" + exit 1 + ;; +esac + +exit 0 diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2bd.install b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2bd.install new file mode 100644 index 00000000000..81f61779928 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2bd.install @@ -0,0 +1,7 @@ +questone2bd/cfg/questone2bd-modules.conf etc/modules-load.d +questone2bd/cfg/questone2bd-modprobe.conf etc/modprobe.d +questone2bd/systemd/platform-modules-questone2bd.service lib/systemd/system +tools/fanctrl/fanctrl.service lib/systemd/system +questone2bd/scripts/get_fan_speed.sh usr/local/bin +questone2bd/scripts/set_fan_speed.sh usr/local/bin +questone2bd/cfg/pid_config_questone2bd.ini usr/local/etc \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2bd.postinst b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2bd.postinst new file mode 100644 index 00000000000..bf009203f2c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-questone2bd.postinst @@ -0,0 +1,7 @@ +depmod -a + +systemctl enable platform-modules-questone2bd.service +systemctl start platform-modules-questone2bd.service + +systemctl enable fanctrl.service +systemctl start fanctrl.service diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/rules b/platform/broadcom/sonic-platform-modules-cel/debian/rules index dd5452ccaa1..f486b18968c 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 questone2bd %: dh $@ @@ -17,13 +17,25 @@ override_dh_auto_build: python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \ cd $(MOD_SRC_DIR); \ done) + make -C $(MOD_SRC_DIR)/tools/ispvme_12.2; + make -C $(MOD_SRC_DIR)/tools/fanctrl; + gcc -std=c99 $(MOD_SRC_DIR)/tools/fpga_prog/fpga_prog.c -o $(MOD_SRC_DIR)/tools/fpga_prog/fpga_prog; override_dh_auto_install: (for mod in $(MODULE_DIRS); do \ dh_installdirs -pplatform-modules-$${mod} \ $(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + dh_installdirs -pplatform-modules-$${mod} \ + /usr/local/bin; \ cp $(MOD_SRC_DIR)/$${mod}/modules/*.ko \ debian/platform-modules-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ + cp $(MOD_SRC_DIR)/tools/ispvme_12.2/ispvm \ + debian/platform-modules-$${mod}/usr/local/bin/; \ + cp $(MOD_SRC_DIR)/tools/fanctrl/fand_v2 \ + debian/platform-modules-$${mod}/usr/local/bin/; \ + ls $(MOD_SRC_DIR)/tools/fpga_prog; \ + cp $(MOD_SRC_DIR)/tools/fpga_prog/fpga_prog \ + debian/platform-modules-$${mod}/usr/local/bin/; \ done) override_dh_usrlocal: @@ -33,4 +45,6 @@ override_dh_clean: (for mod in $(MODULE_DIRS); do \ make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules clean; \ done) + make -C $(MOD_SRC_DIR)/tools/ispvme_12.2 clean; + make -C $(MOD_SRC_DIR)/tools/fanctrl clean; diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/cfg/pid_config_questone2bd.ini b/platform/broadcom/sonic-platform-modules-cel/questone2bd/cfg/pid_config_questone2bd.ini new file mode 100644 index 00000000000..7227dd6273c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/cfg/pid_config_questone2bd.ini @@ -0,0 +1,19 @@ +#[PID thermal control setting] +[PID enable] +PID_enable=0 + +[SWITCH_TEMP] +setpoint = 95 +P = 3 +I = 0.5 +D = 0.5 +min_output = 76 + +[CPU_TEMP] +setpoint = -15 +P = 3 +I = 0.5 +D = 0.5 +min_output = 76 + + diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/cfg/questone2bd-modprobe.conf b/platform/broadcom/sonic-platform-modules-cel/questone2bd/cfg/questone2bd-modprobe.conf new file mode 100644 index 00000000000..57a28ea72ea --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/cfg/questone2bd-modprobe.conf @@ -0,0 +1 @@ +options switchboard_fpga allow_unsafe_i2c_access=1 \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/cfg/questone2bd-modules.conf b/platform/broadcom/sonic-platform-modules-cel/questone2bd/cfg/questone2bd-modules.conf new file mode 100644 index 00000000000..574c48f7a66 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/cfg/questone2bd-modules.conf @@ -0,0 +1,16 @@ +# /etc/modules: kernel modules to load at boot time. +# +# This file contains the names of kernel modules that should be loaded +# at boot time, one per line. Lines beginning with "#" are ignored. + +i2c-i801 +i2c-isch +i2c-ismt +i2c-dev +i2c-mux +i2c-smbus + +i2c-mux-gpio +i2c-mux-pca954x +8021q + diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/Makefile b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/Makefile new file mode 100644 index 00000000000..81adc9ef04e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/Makefile @@ -0,0 +1,2 @@ +obj-m := mc24lc64t.o baseboard_cpld.o switchboard_fpga.o i2c-imc.o dimm-bus.o +obj-m += i2c_dev_sysfs.o syscpld.o fancpld.o dps1100.o diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/baseboard_cpld.c b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/baseboard_cpld.c new file mode 100644 index 00000000000..3edf7eaed04 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/baseboard_cpld.c @@ -0,0 +1,417 @@ +/* + * cpld_b.c - The CPLD driver for the Base Board of questone2bd + * The driver implement sysfs to access CPLD register on the baseboard of questone2bd via LPC bus. + * Copyright (C) 2018 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 "questone2bd.cpldb" +/** + * CPLD register address for read and write. + */ +#define VERSION_ADDR 0xA100 +#define SCRATCH_ADDR 0xA101 +#define SYS_LED_ADDR 0xA162 + +#define CPLD_REGISTER_SIZE 0x7C + +struct cpld_b_data { + struct mutex cpld_lock; + uint16_t read_addr; +}; + +struct cpld_b_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 = 0; + // 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 = 0; + // 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); + +/** + * Read all CPLD register in binary mode. + * @return number of byte read. + */ +static ssize_t dump_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + unsigned long i=0; + ssize_t status; + + mutex_lock(&cpld_data->cpld_lock); +begin: + if(i < count){ + buf[i++] = inb(VERSION_ADDR + off); + off++; + msleep(1); + goto begin; + } + status = count; + + mutex_unlock(&cpld_data->cpld_lock); + return status; +} +static BIN_ATTR_RO(dump, CPLD_REGISTER_SIZE); + +/** + * Show system led status - on/off/1k/4k + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return Hex string read from scratch register. + */ +static ssize_t sys_led_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + data = data & 0x3; + return sprintf(buf, "%s\n", + data == 0x03 ? "off" : data == 0x02 ? "4k" : data ==0x01 ? "1k": "on"); +} + +/** + * Set the status of system led - on/off/1k/4k + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t sys_led_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned char led_status,data; + if(sysfs_streq(buf, "off")){ + led_status = 0x03; + }else if(sysfs_streq(buf, "4k")){ + led_status = 0x02; + }else if(sysfs_streq(buf, "1k")){ + led_status = 0x01; + }else if(sysfs_streq(buf, "on")){ + led_status = 0x00; + }else{ + count = -EINVAL; + return count; + } + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + data = data & ~(0x3); + data = data | led_status; + outb(data, SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_RW(sys_led); + +/** + * Show system led color - both/green/yellow/none + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return Hex string read from scratch register. + */ +static ssize_t sys_led_color_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + data = (data >> 4) & 0x3; + return sprintf(buf, "%s\n", + data == 0x03 ? "off" : data == 0x02 ? "yellow" : data ==0x01 ? "green": "both"); +} + +/** + * Set the color of system led - both/green/yellow/none + * When both color is selected, only blink 1K or 4K is supported. + * + * @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 *cpld_b_attrs[] = { + &dev_attr_version.attr, + &dev_attr_scratch.attr, + &dev_attr_getreg.attr, + &dev_attr_setreg.attr, + &dev_attr_sys_led.attr, + &dev_attr_sys_led_color.attr, + NULL, +}; + +static struct bin_attribute *cpld_b_bin_attrs[] = { + &bin_attr_dump, + NULL, +}; + +static struct attribute_group cpld_b_attrs_grp = { + .attrs = cpld_b_attrs, + .bin_attrs = cpld_b_bin_attrs, +}; + +static struct resource cpld_b_resources[] = { + { + .start = 0xA100, + .end = 0xA175, + .flags = IORESOURCE_IO, + }, +}; + +static void cpld_b_dev_release( struct device * dev) +{ + return; +} + +static struct platform_device cpld_b_dev = { + .name = DRIVER_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(cpld_b_resources), + .resource = cpld_b_resources, + .dev = { + .release = cpld_b_dev_release, + } +}; + +static int cpld_b_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + int err = 0; + + cpld_data = devm_kzalloc(&pdev->dev, sizeof(struct cpld_b_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 -ENODEV; + } + + err = sysfs_create_group(&pdev->dev.kobj, &cpld_b_attrs_grp); + if (err) { + printk(KERN_ERR "Cannot create sysfs for baseboard CPLD\n"); + return err; + } + return 0; +} + +static int cpld_b_drv_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &cpld_b_attrs_grp); + return 0; +} + +static struct platform_driver cpld_b_drv = { + .probe = cpld_b_drv_probe, + .remove = __exit_p(cpld_b_drv_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +int cpld_b_init(void) +{ + // Register platform device and platform driver + platform_device_register(&cpld_b_dev); + platform_driver_register(&cpld_b_drv); + return 0; +} + +void cpld_b_exit(void) +{ + // Unregister platform device and platform driver + platform_driver_unregister(&cpld_b_drv); + platform_device_unregister(&cpld_b_dev); +} + +module_init(cpld_b_init); +module_exit(cpld_b_exit); + + +MODULE_AUTHOR("Pradchaya P. "); +MODULE_DESCRIPTION("Celestica questone2bd CPLD baseboard driver"); +MODULE_VERSION("0.0.2"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/dimm-bus.c b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/dimm-bus.c new file mode 100644 index 00000000000..9f30945e1d1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/dimm-bus.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013-2016 Andrew Lutomirski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include "dimm-bus.h" +static bool probe_addr(struct i2c_adapter *adapter, int addr) +{ + /* + * So far, all known devices that live on DIMMs can be safely + * and reliably detected by trying to read a byte at address + * zero. (The exception is the SPD write protection control, + * which can't be probed and requires special hardware and/or + * quick writes to access, and has no driver.) + */ + union i2c_smbus_data dummy; + return i2c_smbus_xfer(adapter, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE_DATA, &dummy) >= 0; +} +/** + * i2c_scan_dimm_bus() - Scans an SMBUS segment known to contain DIMMs + * @adapter: The SMBUS adapter to scan + * + * This function tells the DIMM-bus code that the adapter is known to + * contain DIMMs. i2c_scan_dimm_bus will probe for devices known to + * live on DIMMs. + * + * Do NOT call this function on general-purpose system SMBUS segments + * unless you know that the only things on the bus are DIMMs. + * Otherwise is it very likely to mis-identify other things on the + * bus. + * + * Callers are advised not to set adapter->class = I2C_CLASS_SPD to + * avoid having two separate mechanisms trying to automatically claim + * devices on the bus. + */ +void i2c_scan_dimm_bus(struct i2c_adapter *adapter) +{ + struct i2c_board_info info = {}; + int slot; + /* + * We probe with "read byte data". If any DIMM SMBUS driver can't + * support that access type, this function should be updated. + */ + if (WARN_ON(!i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA))) + return; + /* + * Addresses on DIMMs use the three low bits to identify the slot + * and the four high bits to identify the device type. Known + * devices include: + * + * - 0x10 - 0x17: NVDIMM controller (pre-standard) + * - 0x18 - 0x1f: TSOD (Temperature Sensor on DIMM) + * - 0x40 - 0x47: JESD245 Byte Addressable Energy Backed Interface + * - 0x50 - 0x57: SPD (Serial Presence Detect) EEPROM + * - 0x30 - 0x37: SPD WP control -- not easy to probe + * + * There's no point in trying to probe the SPD WP control: we'd + * want to probe using quick reads, which i2c-imc doesn't + * support, we don't have a driver for it, we can't really use + * it without special hardware (it's not a normal i2c slave -- + * see the JEDEC docs), and using it risks bricking the DIMM + * it's on anyway. + * + * NB: There's no need to save the return value from + * i2c_new_device, as the core code will unregister it for us + * when the adapter is removed. If users want to bind a + * different driver, nothing stops them from unbinding the + * drivers we request here. + */ + for (slot = 0; slot < 8; slot++) { + /* If there's no SPD, then assume there's no DIMM here. */ + if (!probe_addr(adapter, 0x50 | slot)) + continue; + strcpy(info.type, "ee1004"); + info.addr = 0x50 | slot; + i2c_new_device(adapter, &info); + if (probe_addr(adapter, 0x18 | slot)) { + /* + * This is a temperature sensor. The interface is + * defined in the JEDEC TSE2004av specification. + * Linux's driver for this is called "jc42", which + * is a bit nonsensical (JC-42 is the name of the + * committee, not the sensor). + */ + strcpy(info.type, "jc42"); + info.addr = 0x18 | slot; + i2c_new_device(adapter, &info); + } + } +} +EXPORT_SYMBOL(i2c_scan_dimm_bus); +MODULE_AUTHOR("Andrew Lutomirski "); +MODULE_DESCRIPTION("i2c DIMM bus support"); +MODULE_LICENSE("GPL v2"); + diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/dimm-bus.h b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/dimm-bus.h new file mode 100644 index 00000000000..8f14d5fb973 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/dimm-bus.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2013-2016 Andrew Lutomirski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _I2C_DIMM_BUS +#define _I2C_DIMM_BUS +struct i2c_adapter; +void i2c_scan_dimm_bus(struct i2c_adapter *adapter); +#endif /* _I2C_DIMM_BUS */ + diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/dps1100.c b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/dps1100.c new file mode 100644 index 00000000000..904c94b6066 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/dps1100.c @@ -0,0 +1,918 @@ +/* + * Hardware monitoring driver for DPS1100 and compatibles + * Based on the pfe3000 driver with the following copyright: + * + * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck + * Copyright 2004-present Facebook. All Rights Reserved. + * Copyright 2018 Celestica. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmbus.h" + +#define SYSFS_READ 0 +#define SYSFS_WRITE 1 + +#define DPS1100_PSU_NUM 4 + +#define DPS1100_OP_REG_ADDR PMBUS_OPERATION +#define DPS1100_OP_SHUTDOWN_CMD 0x0 +#define DPS1100_OP_POWERON_CMD 0x80 +#define DPS1100_FAN1_PWM_REG 0x3B +#define DPS1100_FAN1_SPEED_REG 0x90 + + +#define DPS1100_WAIT_TIME 1000 /* uS */ + +#define TO_DPS1100_DATA(x) container_of(x, struct dps1100_data, info) +#define TO_PMBUS_DATA(x) container_of(x, struct pmbus_data, hwmon_dev) +#define TO_I2C_DEV(x) container_of(x, struct device, driver_data) +#define TO_I2C_SYSFS_ATTR(_attr) container_of(_attr, struct sysfs_attr_t, dev_attr) + +/* + * Index into status register array, per status register group + */ +#define PB_STATUS_BASE 0 +#define PB_STATUS_VOUT_BASE (PB_STATUS_BASE + PMBUS_PAGES) +#define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES) +#define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES) +#define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES) +#define PB_STATUS_TEMP_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES) +#define PB_STATUS_INPUT_BASE (PB_STATUS_TEMP_BASE + PMBUS_PAGES) +#define PB_STATUS_VMON_BASE (PB_STATUS_INPUT_BASE + 1) +#define PB_NUM_STATUS_REG (PB_STATUS_VMON_BASE + 1) + + +typedef ssize_t (*i2c_dev_attr_show_fn)(struct device *dev, struct device_attribute *attr, char *buf); +typedef ssize_t (*i2c_dev_attr_store_fn)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); + +#define I2C_DEV_ATTR_SHOW_DEFAULT (i2c_dev_attr_show_fn)(1) +#define I2C_DEV_ATTR_STORE_DEFAULT (i2c_dev_attr_store_fn)(1) + + +struct pmbus_data { + struct device *dev; + struct device *hwmon_dev; + + u32 flags; /* from platform data */ + + /* linear mode: exponent for output voltages */ + int exponent[PMBUS_PAGES]; + + const struct pmbus_driver_info *info; + + int max_attributes; + int num_attributes; + struct attribute_group group; + const struct attribute_group *groups[2]; + + struct pmbus_sensor *sensors; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + ktime_t access; + + /* + * A single status register covers multiple attributes, + * so we keep them all together. + */ + u8 status[PB_NUM_STATUS_REG]; + u8 status_register; + + u8 currpage; +}; + + +enum chips { + DPS550 = 1, + DPS1100, +}; + + +struct alarm_data_t { + int alarm_min; + int alarm_max; +}; + +struct dps1100_alarm_data { + struct alarm_data_t in1; //VIN + struct alarm_data_t in2; //VOUT + struct alarm_data_t fan1; + struct alarm_data_t temp1; + struct alarm_data_t temp2; + struct alarm_data_t pin; + struct alarm_data_t pout; + struct alarm_data_t iin; + struct alarm_data_t iout; +}; + +struct i2c_dev_attr_t { + const char *name; + const char *help; + i2c_dev_attr_show_fn show; + i2c_dev_attr_store_fn store; + int reg; +} ; + + +struct sysfs_attr_t { + struct device_attribute dev_attr; + struct i2c_dev_attr_t *i2c_attr; +}; + +struct dps1100_data { + int id; + int shutdown_state; + int fan1_speed; + int fan1_pct; + struct i2c_client *client; + struct dps1100_alarm_data alarm_data; + struct pmbus_driver_info info; + struct attribute_group attr_group; + struct sysfs_attr_t *sysfs_attr; +}; + +struct dps1100_vin_threshold_t { + int bus; + int vin_min; + int vin_max; +}; + +static const struct i2c_device_id dps1100_id[] = { + {"dps550", DPS550 }, + {"dps1100", DPS1100 }, + { } +}; +struct dps1100_vin_threshold_t dps1100_vin_threshold[DPS1100_PSU_NUM]; + +enum PSU { + PSU1 = 1, + PSU2, +}; +/* + * 0 is not OK, 1 is OK, error codes if fails. + */ +extern int psu_ok(int bus, unsigned short addr); +static ssize_t dps1100_ok(struct i2c_client *client) +{ + if (client == NULL) + return 0; + + // FIXME: This adapter number pass to the the function is not a good + // idea, it can be dynamically assigned and cause problem. + // see syscpld.c:psu_ok() + return psu_ok(client->adapter->nr, client->addr); +} + +static ssize_t dps1100_shutdown_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 read_val = 0; + struct i2c_client *client = to_i2c_client(dev); + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct dps1100_data *data = TO_DPS1100_DATA(info); + + if (dps1100_ok(client) != 1) + return -1; + + //client->flags |= I2C_CLIENT_PEC; + read_val = pmbus_read_byte_data(client, 0, DPS1100_OP_REG_ADDR); + if (read_val >= 0) + { + if (read_val == DPS1100_OP_SHUTDOWN_CMD) + data->shutdown_state = 1; + else + data->shutdown_state = 0; + } + + return sprintf(buf, "%d\n", data->shutdown_state); +} + +static ssize_t dps1100_shutdown_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u8 write_value = 0; + long shutdown = 0; + int rc = 0; + struct i2c_client *client = to_i2c_client(dev); + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct dps1100_data *data = TO_DPS1100_DATA(info); + + if (dps1100_ok(client) != 1) + return -1; + + //client->flags |= I2C_CLIENT_PEC; + if (buf == NULL) { + return -ENXIO; + } + + rc = kstrtol(buf, 0, &shutdown); + if (rc != 0) { + return count; + } + + if (shutdown == 1) { + write_value = DPS1100_OP_SHUTDOWN_CMD; + } else { + write_value = DPS1100_OP_POWERON_CMD; + } + + rc = pmbus_write_byte_data(client, 0, DPS1100_OP_REG_ADDR, write_value); + if (rc == 0) { + data->shutdown_state = 1; + } + + return count; +} + +static ssize_t dps1100_reg_byte_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int read_val = 0; + struct pmbus_data *pdata = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(pdata->dev); + struct sysfs_attr_t *sysfs_attr = TO_I2C_SYSFS_ATTR(attr); + struct i2c_dev_attr_t *dev_attr = sysfs_attr->i2c_attr; + + if (dps1100_ok(client) != 1) + return -1; + + read_val = pmbus_read_byte_data(client, 0, dev_attr->reg); + if (read_val < 0) + return read_val; + + return sprintf(buf, "%d\n", read_val); +} + +static ssize_t dps1100_reg_byte_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0; + u8 write_value = 0; + struct pmbus_data *pdata = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(pdata->dev); + struct sysfs_attr_t *sysfs_attr = TO_I2C_SYSFS_ATTR(attr); + struct i2c_dev_attr_t *dev_attr = sysfs_attr->i2c_attr; + + if (dps1100_ok(client) != 1) + return -1; + + //client->flags |= I2C_CLIENT_PEC; + if (buf == NULL) { + return -ENXIO; + } + + rc = kstrtou8(buf, 0, &write_value); + if (rc != 0) { + return count; + } + + rc = pmbus_write_byte_data(client, 0, dev_attr->reg, write_value); + if (rc < 0) { + return rc; + } + + return count; +} + + + +static ssize_t dps1100_vin_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i; + int read_val = 0; + int bus = -1; + struct pmbus_data *pdata = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(pdata->dev); + struct sysfs_attr_t *sysfs_attr = TO_I2C_SYSFS_ATTR(attr); + struct i2c_dev_attr_t *dev_attr = sysfs_attr->i2c_attr; + + if (client->adapter) + bus = client->adapter->nr; + + if (bus < 0) + return -1; + + for (i = 0; i < DPS1100_PSU_NUM; i++) { + if (dps1100_vin_threshold[i].bus == bus) { + if (dev_attr->reg == 0) + read_val = dps1100_vin_threshold[i].vin_min; + else + read_val = dps1100_vin_threshold[i].vin_max; + break; + } + } + + return sprintf(buf, "%d\n", read_val); +} + +static ssize_t dps1100_vin_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int i; + int rc = 0; + int write_value = 0; + int bus = -1; + struct pmbus_data *pdata = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(pdata->dev); + struct sysfs_attr_t *sysfs_attr = TO_I2C_SYSFS_ATTR(attr); + struct i2c_dev_attr_t *dev_attr = sysfs_attr->i2c_attr; + + + if (buf == NULL) { + return -ENXIO; + } + + if (client->adapter) + bus = client->adapter->nr; + + if (bus < 0) + return -1; + + rc = kstrtoint(buf, 0, &write_value); + if (rc != 0) { + return count; + } + + for (i = 0; i < DPS1100_PSU_NUM; i++) { + if (dps1100_vin_threshold[i].bus == bus) { + if (dev_attr->reg == 0) + dps1100_vin_threshold[i].vin_min = write_value; + else + dps1100_vin_threshold[i].vin_max = write_value; + return count; + } + } + for (i = 0; i < DPS1100_PSU_NUM; i++) { + if (dps1100_vin_threshold[i].bus == 0) { + if (dev_attr->reg == 0) + dps1100_vin_threshold[i].vin_min = write_value; + else + dps1100_vin_threshold[i].vin_max = write_value; + dps1100_vin_threshold[i].bus = bus; + break; + } + } + + return count; +} + + +static ssize_t dps1100_reg_word_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int read_val = 0; + struct pmbus_data *pdata = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(pdata->dev); + struct sysfs_attr_t *sysfs_attr = TO_I2C_SYSFS_ATTR(attr); + struct i2c_dev_attr_t *dev_attr = sysfs_attr->i2c_attr; + + if (dps1100_ok(client) != 1) + return -1; + + read_val = pmbus_read_word_data(client, 0, dev_attr->reg); + if (read_val < 0) + { + return read_val; + } + + return sprintf(buf, "%d\n", read_val); +} + +static ssize_t dps1100_reg_word_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0; + u16 write_value = 0; + struct pmbus_data *pdata = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(pdata->dev); + struct sysfs_attr_t *sysfs_attr = TO_I2C_SYSFS_ATTR(attr); + struct i2c_dev_attr_t *dev_attr = sysfs_attr->i2c_attr; + + if (dps1100_ok(client) != 1) + return -1; + + //client->flags |= I2C_CLIENT_PEC; + if (buf == NULL) { + return -ENXIO; + } + + rc = kstrtou16(buf, 0, &write_value); + if (rc != 0) { + return count; + } + + rc = pmbus_write_word_data(client, 0, dev_attr->reg, write_value); + if (rc < 0) { + return rc; + } + + return count; +} + +static struct i2c_dev_attr_t psu_attr_table[] = { + { + "in1_min", + NULL, + dps1100_vin_threshold_show, + dps1100_vin_threshold_store, + 0, + }, + { + "in1_max", + NULL, + dps1100_vin_threshold_show, + dps1100_vin_threshold_store, + 1, + }, + { + "in2_min", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "in2_max", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "fan1_min", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "fan1_max", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "temp1_max_hyst", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "temp1_max", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "temp2_max_hyst", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "temp2_max", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "power1_min", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "power1_max", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "power2_min", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "power2_max", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "curr1_min", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "curr1_max", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "curr2_min", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "curr2_max", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0, + }, + { + "fan1_cfg", + NULL, + dps1100_reg_byte_show, + dps1100_reg_byte_store, + 0x3a, + }, + { + "fan1_pct", + NULL, + dps1100_reg_word_show, + dps1100_reg_word_store, + 0x3b, + }, + +}; + + +static struct pmbus_platform_data platform_data = { + .flags = PMBUS_SKIP_STATUS_CHECK, +}; + +static int sysfs_value_rw(unsigned int *reg, int opcode, int val) +{ + if (opcode == SYSFS_READ) + return *reg; + else if (opcode == SYSFS_WRITE) + *reg = val; + else + return -EINVAL; + + return 0; +} +static ssize_t i2c_dev_sysfs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int val; + struct sysfs_attr_t *sysfs_attr = TO_I2C_SYSFS_ATTR(attr); + struct i2c_dev_attr_t *dev_attr = sysfs_attr->i2c_attr; + + if (!dev_attr->show) { + return -EOPNOTSUPP; + } + + if (dev_attr->show != I2C_DEV_ATTR_SHOW_DEFAULT) { + return dev_attr->show(dev, attr, buf); + } + val = sysfs_value_rw(&dev_attr->reg, SYSFS_READ, 0); + if (val < 0) { + return val; + } + + return sprintf(buf, "%d\n", val); +} + +static ssize_t i2c_dev_sysfs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val; + int ret; + struct sysfs_attr_t *sysfs_attr = TO_I2C_SYSFS_ATTR(attr); + struct i2c_dev_attr_t *dev_attr = sysfs_attr->i2c_attr; + + if (!dev_attr->store) { + return -EOPNOTSUPP; + } + + if (dev_attr->store != I2C_DEV_ATTR_STORE_DEFAULT) { + return dev_attr->store(dev, attr, buf, count); + } + + ret = kstrtouint(buf, 0, &val); + if (ret != 0) { + return -EINVAL; + } + + ret = sysfs_value_rw(&dev_attr->reg, SYSFS_WRITE, val); + if (ret < 0) { + return ret; + } + + return count; +} + + +static int i2c_dev_sysfs_data_clean(struct device *dev, struct dps1100_data *data) +{ + if (!data) { + return 0; + } + + if (data->attr_group.attrs) { + sysfs_remove_group(&dev->kobj, &data->attr_group); + kfree(data->attr_group.attrs); + } + if (data->sysfs_attr) { + kfree(data->sysfs_attr); + } + + return 0; +} + +static int dps1100_register_sysfs(struct device *dev, + struct dps1100_data *data, + struct i2c_dev_attr_t *dev_attrs, + int n_attrs) +{ + int i; + int ret; + mode_t mode; + struct sysfs_attr_t *cur_attr; + struct i2c_dev_attr_t *cur_dev_attr; + struct attribute **cur_grp_attr; + + data->sysfs_attr = kzalloc(sizeof(*data->sysfs_attr) * n_attrs, GFP_KERNEL); + data->attr_group.attrs = kzalloc(sizeof(*data->attr_group.attrs) * (n_attrs + 1), GFP_KERNEL); + if (!data->sysfs_attr || !data->attr_group.attrs) { + ret = -ENOMEM; + goto exit_cleanup; + } + + cur_attr = &data->sysfs_attr[0]; + cur_grp_attr = &data->attr_group.attrs[0]; + cur_dev_attr = dev_attrs; + for (i = 0; i < n_attrs; i++, cur_attr++, cur_grp_attr++, cur_dev_attr++) { + mode = S_IRUGO; + if (cur_dev_attr->store) { + mode |= S_IWUSR; + } + cur_attr->dev_attr.attr.name = cur_dev_attr->name; + cur_attr->dev_attr.attr.mode = mode; + cur_attr->dev_attr.show = i2c_dev_sysfs_show; + cur_attr->dev_attr.store = i2c_dev_sysfs_store; + *cur_grp_attr = &cur_attr->dev_attr.attr; + cur_attr->i2c_attr = cur_dev_attr; + } + + ret = sysfs_create_group(&dev->kobj, &data->attr_group); + if (ret < 0) { + goto exit_cleanup; + } + + return 0; +exit_cleanup: + i2c_dev_sysfs_data_clean(dev, data); + return ret; +} + +static void dps1100_remove_sysfs(struct i2c_client *client) +{ + struct pmbus_data *pdata = (struct pmbus_data *)i2c_get_clientdata(client); + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct dps1100_data *data = TO_DPS1100_DATA(info); + + i2c_dev_sysfs_data_clean(pdata->hwmon_dev, data); + return; +} + +static DEVICE_ATTR(shutdown, S_IRUGO | S_IWUSR, + NULL, dps1100_shutdown_store); + +static struct attribute *shutdown_attrs[] = { + &dev_attr_shutdown.attr, + NULL +}; +static struct attribute_group control_attr_group = { + .name = "control", + .attrs = shutdown_attrs, +}; + + +static int dps1100_register_shutdown(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return sysfs_create_group(&client->dev.kobj, &control_attr_group); +} + +static void dps1100_remove_shutdown(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &control_attr_group); + return; +} + +static int dps1100_remove(struct i2c_client *client) +{ + dps1100_remove_shutdown(client); + dps1100_remove_sysfs(client); + return pmbus_do_remove(client); +} + +static int dps1100_pmbus_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + /* + * This mask out the auto probe sensor limits, + * Since we want to use our custom limits. + */ + if (reg >= PMBUS_VIRT_BASE + || reg == PMBUS_VIN_UV_WARN_LIMIT + || reg == PMBUS_VIN_UV_FAULT_LIMIT + || reg == PMBUS_VIN_OV_WARN_LIMIT + || reg == PMBUS_VIN_OV_FAULT_LIMIT + || reg == PMBUS_VOUT_UV_WARN_LIMIT + || reg == PMBUS_VOUT_UV_FAULT_LIMIT + || reg == PMBUS_VOUT_OV_FAULT_LIMIT + || reg == PMBUS_VOUT_OV_WARN_LIMIT + || reg == PMBUS_IIN_OC_WARN_LIMIT + || reg == PMBUS_IIN_OC_FAULT_LIMIT + || reg == PMBUS_IOUT_OC_WARN_LIMIT + || reg == PMBUS_IOUT_UC_FAULT_LIMIT + || reg == PMBUS_IOUT_OC_FAULT_LIMIT + || reg == PMBUS_PIN_OP_WARN_LIMIT + || reg == PMBUS_POUT_MAX + || reg == PMBUS_POUT_OP_WARN_LIMIT + || reg == PMBUS_POUT_OP_FAULT_LIMIT + || reg == PMBUS_UT_WARN_LIMIT + || reg == PMBUS_UT_FAULT_LIMIT + || reg == PMBUS_OT_WARN_LIMIT + || reg == PMBUS_OT_FAULT_LIMIT) + return -ENXIO; + + if (dps1100_ok(client) != 1) + return -1; + ret = pmbus_read_word_data(client, page, reg); + + return ret; +} + +static int dps1100_pmbus_write_word_data(struct i2c_client *client, int page, int reg, u16 word) +{ + int ret; + + if (dps1100_ok(client) != 1) + return -1; + + ret = pmbus_write_word_data(client, page, reg, word); + + return ret; +} + +static int dps1100_pmbus_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + if (dps1100_ok(client) != 1) + return -1; + + ret = pmbus_read_byte_data(client, page, reg); + + return ret; +} + +static int dps1100_pmbus_write_byte(struct i2c_client *client, int page, u8 value) +{ + int ret; + + if (dps1100_ok(client) != 1) + return -1; + + ret = pmbus_write_byte(client, page, value); + + return ret; +} + +static int dps1100_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + int n_attrs; + struct dps1100_data *data; + struct pmbus_driver_info *info; + struct pmbus_data *pdata; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(struct dps1100_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->shutdown_state = 0; + data->client = client; + + info = &data->info; + info->pages = 1; + + info->func[0] = PMBUS_HAVE_VIN + | PMBUS_HAVE_VOUT + | PMBUS_HAVE_IOUT + | PMBUS_HAVE_TEMP + | PMBUS_HAVE_PIN + | PMBUS_HAVE_POUT + | PMBUS_HAVE_FAN12 + | PMBUS_HAVE_IIN + | PMBUS_HAVE_TEMP2; + + info->read_word_data = dps1100_pmbus_read_word_data; + info->write_word_data = dps1100_pmbus_write_word_data; + info->read_byte_data = dps1100_pmbus_read_byte_data; + info->write_byte = dps1100_pmbus_write_byte; + + ret = pmbus_do_probe(client, id, info); + if (ret < 0) { + dev_err(&client->dev, "pmbus probe error\n"); + return -EIO; + } + pdata = (struct pmbus_data *)i2c_get_clientdata(client); + if (pdata) { + n_attrs = sizeof(psu_attr_table) / sizeof(psu_attr_table[0]); + ret = dps1100_register_sysfs(pdata->hwmon_dev, data, psu_attr_table, n_attrs); + if (ret < 0) { + dev_err(&client->dev, "Unsupported alarm sysfs operation\n"); + return -EIO; + } + } + + ret = dps1100_register_shutdown(client, id); + if (ret < 0) { + dev_err(&client->dev, "Unsupported shutdown operation\n"); + return -EIO; + } + + return 0; +} + + +static struct i2c_driver dps1100_driver = { + .driver = { + .name = "dps1100", + }, + .probe = dps1100_probe, + .remove = dps1100_remove, + .id_table = dps1100_id, +}; + +module_i2c_driver(dps1100_driver); + +MODULE_AUTHOR("Micky Zhan, based on work by Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for DPS1100"); +MODULE_VERSION("0.0.3"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/fancpld.c b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/fancpld.c new file mode 100644 index 00000000000..1594c51fc73 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/fancpld.c @@ -0,0 +1,707 @@ +/* + * FAN CPLD driver for CPLD and compatibles + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "i2c_dev_sysfs.h" + +#define DEBUG + +#define FANCPLD_ALARM_NODE 0xff /*just for flag using*/ +#define SYSFS_READ 0 +#define SYSFS_WRITE 1 + +#ifdef DEBUG +#define FANCPLD_DEBUG(fmt, ...) do { \ + printk(KERN_DEBUG "%s:%d " fmt "\n", \ + __FUNCTION__, __LINE__, ##__VA_ARGS__); \ +} while (0) + +#else /* !DEBUG */ + +#define FANCPLD_DEBUG(fmt, ...) +#endif + +struct alarm_data_t { + int alarm_min; + int alarm_max; +}; + +struct ir358x_alarm_data { + struct alarm_data_t fan1; + struct alarm_data_t fan2; + struct alarm_data_t fan3; + struct alarm_data_t fan4; + struct alarm_data_t fan5; + struct alarm_data_t fan6; + struct alarm_data_t fan7; + struct alarm_data_t fan8; + struct alarm_data_t fan9; + struct alarm_data_t fan10; +}; + +static i2c_dev_data_st fancpld_data; +struct ir358x_alarm_data fancpld_alarm_data; + + +static int alarm_value_rw(const char *name, int opcode, int value) +{ + int *p = NULL; + + if (strcmp(name, "fan1_min") == 0) { + p = &fancpld_alarm_data.fan1.alarm_min; + } else if (strcmp(name, "fan1_max") == 0) { + p = &fancpld_alarm_data.fan1.alarm_max; + } else if (strcmp(name, "fan2_min") == 0) { + p = &fancpld_alarm_data.fan2.alarm_min; + } else if (strcmp(name, "fan2_max") == 0) { + p = &fancpld_alarm_data.fan2.alarm_max; + } else if (strcmp(name, "fan3_min") == 0) { + p = &fancpld_alarm_data.fan3.alarm_min; + } else if (strcmp(name, "fan3_max") == 0) { + p = &fancpld_alarm_data.fan3.alarm_max; + } else if (strcmp(name, "fan4_min") == 0) { + p = &fancpld_alarm_data.fan4.alarm_min; + } else if (strcmp(name, "fan4_max") == 0) { + p = &fancpld_alarm_data.fan4.alarm_max; + } else if (strcmp(name, "fan5_min") == 0) { + p = &fancpld_alarm_data.fan5.alarm_min; + } else if (strcmp(name, "fan5_max") == 0) { + p = &fancpld_alarm_data.fan5.alarm_max; + } else if (strcmp(name, "fan6_min") == 0) { + p = &fancpld_alarm_data.fan6.alarm_min; + } else if (strcmp(name, "fan6_max") == 0) { + p = &fancpld_alarm_data.fan6.alarm_max; + } else if (strcmp(name, "fan7_min") == 0) { + p = &fancpld_alarm_data.fan7.alarm_min; + } else if (strcmp(name, "fan7_max") == 0) { + p = &fancpld_alarm_data.fan7.alarm_max; + } else if (strcmp(name, "fan8_min") == 0) { + p = &fancpld_alarm_data.fan8.alarm_min; + } else if (strcmp(name, "fan8_max") == 0) { + p = &fancpld_alarm_data.fan8.alarm_max; + } else if (strcmp(name, "fan9_min") == 0) { + p = &fancpld_alarm_data.fan9.alarm_min; + } else if (strcmp(name, "fan9_max") == 0) { + p = &fancpld_alarm_data.fan9.alarm_max; + } else if (strcmp(name, "fan10_min") == 0) { + p = &fancpld_alarm_data.fan10.alarm_min; + } else if (strcmp(name, "fan10_max") == 0) { + p = &fancpld_alarm_data.fan10.alarm_max; + } else { + return -1; + } + + if (opcode == SYSFS_READ) + return *p; + else if (opcode == SYSFS_WRITE) + *p = value; + else + return -1; + + return 0; +} + + +static ssize_t fan_rpm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value = -1; + + value = i2c_dev_read_byte(dev, attr); + + if (value < 0) { + FANCPLD_DEBUG("I2C read error!\n"); + return -1; + } + + + return scnprintf(buf, PAGE_SIZE, "%d\n", value * 150); +} + +static ssize_t fan_alarm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value = -1; + i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr); + const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr; + const char *name = dev_attr->ida_name; + + if (!name) + return -1; + + value = alarm_value_rw(name, SYSFS_READ, 0); + + return scnprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t fan_alarm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + int write_value = 0; + i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr); + const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr; + const char *name = dev_attr->ida_name; + + if (!name) + return -1; + + if (buf == NULL) { + return -ENXIO; + } + + rc = kstrtoint(buf, 10, &write_value); + if (rc != 0) { + return count; + } + rc = alarm_value_rw(name, SYSFS_WRITE, write_value); + if (rc < 0) + return -1; + + return count; +} + + + +enum chips { + FANCPLD = 1, +}; + + +static const struct i2c_device_id fancpld_id[] = { + {"fancpld", FANCPLD }, + { } +}; + + +static const i2c_dev_attr_st fancpld_attr_table[] = { + { + "version", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x0, 0, 8, + }, + { + "scratch", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x4, 0, 8, + }, + { + "int_status", + "0x0: interrupt\n" + "0x1: not interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x6, 0, 1, + }, + { + "int_result", + "0x0: not interrupt\n" + "0x1: interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x7, 0, 1, + }, + { + "eeprom_wp", + "0x0: protect\n" + "0x1: not protect", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x9, 0, 1, + }, + { + "wdt_en", + "0x0: fan wdt disable\n" + "0x1: fan wdt enable", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0xc, 0, 1, + }, + { + "wdt_status", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0xc, 4, 1, + }, + { + "fan1_input", + NULL, + fan_rpm_show, + NULL, + 0x20, 0, 8, + }, + { + "fan2_input", + NULL, + fan_rpm_show, + NULL, + 0x21, 0, 8, + }, + { + "fan1_pwm", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x22, 0, 8, + }, + { + "fan1_led", + "0x1: green\n" + "0x2: red\n" + "0x3: off", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x24, 0, 2, + }, + { + "fan1_eeprom_wp", + "0x0: protect\n" + "0x1: not protect", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x25, 0, 1, + }, + { + "fan1_present", + "0x0: present\n" + "0x1: absent", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x26, 0, 1, + }, + { + "fan1_dir", + "0x0: F2B\n" + "0x1: B2F", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x26, 1, 1, + }, + { + "fan3_input", + NULL, + fan_rpm_show, + NULL, + 0x40, 0, 8, + }, + { + "fan4_input", + NULL, + fan_rpm_show, + NULL, + 0x41, 0, 8, + }, + { + "fan2_pwm", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x42, 0, 8, + }, + { + "fan2_led", + "0x1: green\n" + "0x2: red\n" + "0x3: off", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x44, 0, 2, + }, + { + "fan2_eeprom_wp", + "0x0: protect\n" + "0x1: not protect", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x45, 0, 1, + }, + { + "fan2_present", + "0x0: present\n" + "0x1: absent", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x46, 0, 1, + }, + { + "fan2_dir", + "0x0: F2B\n" + "0x1: B2F", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x46, 1, 1, + }, + { + "fan5_input", + NULL, + fan_rpm_show, + NULL, + 0x60, 0, 8, + }, + { + "fan6_input", + NULL, + fan_rpm_show, + NULL, + 0x61, 0, 8, + }, + { + "fan3_pwm", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x62, 0, 8, + }, + { + "fan3_led", + "0x1: green\n" + "0x2: red\n" + "0x3: off", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x64, 0, 2, + }, + { + "fan3_eeprom_wp", + "0x0: protect\n" + "0x1: not protect", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x65, 0, 1, + }, + { + "fan3_present", + "0x0: present\n" + "0x1: absent", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x66, 0, 1, + }, + { + "fan3_dir", + "0x0: F2B\n" + "0x1: B2F", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x66, 1, 1, + }, + { + "fan7_input", + NULL, + fan_rpm_show, + NULL, + 0x80, 0, 8, + }, + { + "fan8_input", + NULL, + fan_rpm_show, + NULL, + 0x81, 0, 8, + }, + { + "fan4_pwm", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x82, 0, 8, + }, + { + "fan4_led", + "0x1: green\n" + "0x2: red\n" + "0x3: off", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x84, 0, 2, + }, + { + "fan4_eeprom_wp", + "0x0: protect\n" + "0x1: not protect", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x85, 0, 1, + }, + { + "fan4_present", + "0x0: present\n" + "0x1: absent", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x86, 0, 1, + }, + { + "fan4_dir", + "0x0: F2B\n" + "0x1: B2F", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x86, 1, 1, + }, + { + "fan9_input", + NULL, + fan_rpm_show, + NULL, + 0xa0, 0, 8, + }, + { + "fan10_input", + NULL, + fan_rpm_show, + NULL, + 0xa1, 0, 8, + }, + { + "fan5_pwm", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0xa2, 0, 8, + }, + { + "fan5_led", + "0x1: green\n" + "0x2: red\n" + "0x3: off", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0xa4, 0, 2, + }, + { + "fan5_eeprom_wp", + "0x0: protect\n" + "0x1: not protect", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0xa5, 0, 1, + }, + { + "fan5_present", + "0x0: present\n" + "0x1: absent", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0xa6, 0, 1, + }, + { + "fan5_dir", + "0x0: F2B\n" + "0x1: B2F", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0xa6, 1, 1, + }, + { + "fan1_min", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan1_max", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan2_min", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan2_max", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan3_min", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan3_max", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan4_min", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan4_max", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan5_min", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan5_max", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan6_min", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan6_max", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan7_min", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan7_max", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan8_min", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan8_max", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan9_min", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan9_max", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan10_min", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + { + "fan10_max", + NULL, + fan_alarm_show, + fan_alarm_store, + FANCPLD_ALARM_NODE, 0, 0, + }, + +}; + +static int fancpld_remove(struct i2c_client *client) +{ + i2c_dev_sysfs_data_clean(client, &fancpld_data); + return 0; +} + +static int fancpld_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int n_attrs; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA)) + return -ENODEV; + + n_attrs = sizeof(fancpld_attr_table) / sizeof(fancpld_attr_table[0]); + + return i2c_dev_sysfs_data_init(client, &fancpld_data, fancpld_attr_table, n_attrs); +} + + +static struct i2c_driver fancpld_driver = { + .driver = { + .name = "fancpld", + }, + .probe = fancpld_probe, + .remove = fancpld_remove, + .id_table = fancpld_id, +}; + +module_i2c_driver(fancpld_driver); + +MODULE_AUTHOR("Micky Zhan@Celestica.com"); +MODULE_DESCRIPTION("Fan CPLD driver"); +MODULE_VERSION("0.0.1"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/i2c-imc.c b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/i2c-imc.c new file mode 100644 index 00000000000..7b053f43916 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/i2c-imc.c @@ -0,0 +1,556 @@ +/* + * Copyright (c) 2013-2016 Andrew Lutomirski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include "dimm-bus.h" + +/* + * The datasheet can be found here, for example: + * http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/xeon-e5-1600-2600-vol-2-datasheet.pdf + * + * There seem to be quite a few bugs or spec errors, though: + * + * - A successful transaction sets WOD and RDO. + * + * - The docs for TSOD_POLL_EN make no sense (see imc_channel_claim). + * + * - Erratum BT109, which says: + * + * The processor may not complete SMBus (System Management Bus) + * transactions targeting the TSOD (Temperature Sensor On DIMM) + * when Package C-States are enabled. Due to this erratum, if the + * processor transitions into a Package C-State while an SMBus + * transaction with the TSOD is in process, the processor will + * suspend receipt of the transaction. The transaction completes + * while the processor is in a Package C-State. Upon exiting + * Package C-State, the processor will attempt to resume the + * SMBus transaction, detect a protocol violation, and log an + * error. + * + * The description notwithstanding, I've seen difficult-to-reproduce + * issues when the system goes completely idle (so package C-states can + * be entered) while software-initiated SMBUS transactions are in + * progress. + */ + +/* Register offsets (in PCI configuration space) */ +#define SMBSTAT(i) (0x180 + 0x10*(i)) +#define SMBCMD(i) (0x184 + 0x10*(i)) +#define SMBCNTL(i) (0x188 + 0x10*(i)) +#define SMB_TSOD_POLL_RATE_CNTR(i) (0x18C + 0x10*(i)) +#define SMB_TSOD_POLL_RATE (0x1A8) + +/* SMBSTAT fields */ +#define SMBSTAT_RDO (1U << 31) /* Read Data Valid */ +#define SMBSTAT_WOD (1U << 30) /* Write Operation Done */ +#define SMBSTAT_SBE (1U << 29) /* SMBus Error */ +#define SMBSTAT_SMB_BUSY (1U << 28) /* SMBus Busy State */ +/* 26:24 is the last automatically polled TSOD address */ +#define SMBSTAT_RDATA_MASK 0xffff /* result of a read */ + +/* SMBCMD fields */ +#define SMBCMD_TRIGGER (1U << 31) /* CMD Trigger */ +#define SMBCMD_PNTR_SEL (1U << 30) /* HW polls TSOD with pointer */ +#define SMBCMD_WORD_ACCESS (1U << 29) /* word (vs byte) access */ +#define SMBCMD_TYPE_MASK (3U << 27) /* Mask for access type */ +#define SMBCMD_TYPE_READ (0U << 27) /* Read */ +#define SMBCMD_TYPE_WRITE (1U << 27) /* Write */ +#define SMBCMD_TYPE_PNTR_WRITE (3U << 27) /* Write to pointer */ +#define SMBCMD_SA_MASK (7U << 24) /* Slave Address high bits */ +#define SMBCMD_SA_SHIFT 24 +#define SMBCMD_BA_MASK 0xff0000 /* Bus Txn address */ +#define SMBCMD_BA_SHIFT 16 +#define SMBCMD_WDATA_MASK 0xffff /* data to write */ + +/* SMBCNTL fields */ +#define SMBCNTL_DTI_MASK 0xf0000000 /* Slave Address low bits */ +#define SMBCNTL_DTI_SHIFT 28 /* Slave Address low bits */ +#define SMBCNTL_CKOVRD (1U << 27) /* # Clock Override */ +#define SMBCNTL_DIS_WRT (1U << 26) /* Disable Write (sadly) */ +#define SMBCNTL_SOFT_RST (1U << 10) /* Soft Reset */ +#define SMBCNTL_TSOD_POLL_EN (1U << 8) /* TSOD Polling Enable */ +/* Bits 0-3 and 4-6 indicate TSOD presence in various slots */ + +/* Bits that might randomly change if we race with something. */ +#define SMBCMD_OUR_BITS (~(u32)SMBCMD_TRIGGER) +#define SMBCNTL_OUR_BITS (SMBCNTL_DTI_MASK | SMBCNTL_TSOD_POLL_EN) + +/* System Address Controller, PCI dev 13 fn 6, 8086.3cf5 */ +#define SAD_CONTROL 0xf4 + +#define PCI_DEVICE_ID_INTEL_SBRIDGE_BR 0x3cf5 /* 13.6 */ +#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA 0x3ca8 /* 15.0 */ + +#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TA 0x6fa8 +#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TM 0x6f71 +#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TA 0x6f68 +#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TM 0x6f79 + +static atomic_t imc_raced; /* Set permanently to 1 if we screw up. */ + +static bool allow_unsafe_access; + +struct imc_channel { + struct i2c_adapter adapter; + struct mutex mutex; /* protects access to regs and prev_tsod_poll */ + bool can_write, suspended; + bool prev_tsod_poll; +}; + +struct imc_priv { + struct pci_dev *pci_dev; + struct imc_channel channels[2]; +}; + +static bool imc_wait_not_busy(struct imc_priv *priv, int chan, u32 *stat) +{ + /* + * The clock is around 100kHz, and transactions are nine cycles + * per byte plus a few start/stop cycles, plus whatever clock + * streching is involved. This means that polling every 70us + * or so will give decent performance. + * + * Ideally we would calculate a good estimate for the + * transaction time and sleep, but busy-waiting is an effective + * workaround for an apparent Sandy Bridge bug that causes bogus + * output if the system enters a package C-state. (NB: these + * states are systemwide -- we don't need be running on the + * right package for this to work.) + * + * When Ivy Bridge and Haswell support are added, we could + * consider making the busy-wait depend on the platform. + */ + + int i; + + for (i = 0; i < 50; i++) { + pci_read_config_dword(priv->pci_dev, SMBSTAT(chan), stat); + if (!(*stat & SMBSTAT_SMB_BUSY)) + return true; + udelay(70); /* see comment above -- we need to busy-wait */ + } + + return false; +} + +static void imc_channel_release(struct imc_priv *priv, int chan) +{ + /* Return to HW control. */ + if (priv->channels[chan].prev_tsod_poll) { + u32 cntl; + + pci_read_config_dword(priv->pci_dev, SMBCNTL(chan), &cntl); + cntl |= SMBCNTL_TSOD_POLL_EN; + pci_write_config_dword(priv->pci_dev, SMBCNTL(chan), cntl); + } +} + +static int imc_channel_claim(struct imc_priv *priv, int chan) +{ + /* + * The docs are a bit confused here. We're supposed to disable TSOD + * polling, then wait for busy to be cleared, then set + * SMBCNTL_TSOD_POLL_EN to zero to switch to software control. But + * SMBCNTL_TSOD_POLL_EN is the only documented way to turn off polling. + */ + + u32 cntl, stat; + + if (priv->channels[chan].suspended) + return -EIO; + + pci_read_config_dword(priv->pci_dev, SMBCNTL(chan), &cntl); + priv->channels[chan].prev_tsod_poll = !!(cntl & SMBCNTL_TSOD_POLL_EN); + cntl &= ~SMBCNTL_TSOD_POLL_EN; + pci_write_config_dword(priv->pci_dev, SMBCNTL(chan), cntl); + + /* Sometimes the hardware won't let go. */ + pci_read_config_dword(priv->pci_dev, SMBCNTL(chan), &cntl); + if (cntl & SMBCNTL_TSOD_POLL_EN) + return -EBUSY; + + if (!imc_wait_not_busy(priv, chan, &stat)) { + imc_channel_release(priv, chan); + return -EBUSY; /* Someone else is controlling the bus. */ + } + + return 0; /* The channel is ours. */ +} + +static bool imc_channel_can_claim(struct imc_priv *priv, int chan) +{ + u32 orig_cntl, cntl; + + /* See if we can turn off TSOD_POLL_EN. */ + + pci_read_config_dword(priv->pci_dev, SMBCNTL(chan), &orig_cntl); + pci_write_config_dword(priv->pci_dev, SMBCNTL(chan), + orig_cntl & ~SMBCNTL_TSOD_POLL_EN); + + pci_read_config_dword(priv->pci_dev, SMBCNTL(chan), &cntl); + if (cntl & SMBCNTL_TSOD_POLL_EN) + return false; /* Failed. */ + + pci_write_config_dword(priv->pci_dev, SMBCNTL(chan), orig_cntl); + return true; +} + +/* + * The iMC supports five access types. The terminology is rather + * inconsistent. These are the types: + * + * "Write to pointer register SMBus": I2C_SMBUS_WRITE, I2C_SMBUS_BYTE + * + * Read byte/word: I2C_SMBUS_READ, I2C_SMBUS_{BYTE|WORD}_DATA + * + * Write byte/word: I2C_SMBUS_WRITE, I2C_SMBUS_{BYTE|WORD}_DATA + * + * The pointer write operations is AFAICT completely useless for + * software control, for two reasons. First, HW periodically polls any + * TSODs on the bus, so it will corrupt the pointer in between SW + * transactions. More importantly, the matching "read byte"/"receive + * byte" (the address-less single-byte read) is not available for SW + * control. Therefore, this driver doesn't implement pointer writes + * + * There is no PEC support. + */ + +static u32 imc_func(struct i2c_adapter *adapter) +{ + int chan; + struct imc_channel *ch; + struct imc_priv *priv = i2c_get_adapdata(adapter); + + chan = (adapter == &priv->channels[0].adapter ? 0 : 1); + ch = &priv->channels[chan]; + + if (ch->can_write) + return I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA; + else + return I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA; +} + +static s32 imc_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + int ret, chan; + u32 cmd = 0, cntl, final_cmd, final_cntl, stat; + struct imc_channel *ch; + struct imc_priv *priv = i2c_get_adapdata(adap); + + if (atomic_read(&imc_raced)) + return -EIO; /* Minimize damage. */ + + chan = (adap == &priv->channels[0].adapter ? 0 : 1); + ch = &priv->channels[chan]; + + /* Encode CMD part of addresses and access size */ + cmd |= ((u32)addr & 0x7) << SMBCMD_SA_SHIFT; + cmd |= ((u32)command) << SMBCMD_BA_SHIFT; + if (size == I2C_SMBUS_WORD_DATA) + cmd |= SMBCMD_WORD_ACCESS; + + /* Encode read/write and data to write */ + if (read_write == I2C_SMBUS_READ) { + cmd |= SMBCMD_TYPE_READ; + } else { + cmd |= SMBCMD_TYPE_WRITE; + cmd |= (size == I2C_SMBUS_WORD_DATA + ? swab16(data->word) + : data->byte); + } + + mutex_lock(&ch->mutex); + + ret = imc_channel_claim(priv, chan); + if (ret) + goto out_unlock; + + pci_read_config_dword(priv->pci_dev, SMBCNTL(chan), &cntl); + cntl &= ~SMBCNTL_DTI_MASK; + cntl |= ((u32)addr >> 3) << SMBCNTL_DTI_SHIFT; + pci_write_config_dword(priv->pci_dev, SMBCNTL(chan), cntl); + + /* + * This clears SMBCMD_PNTR_SEL. We leave it cleared so that we don't + * need to think about keeping the TSOD pointer state consistent with + * the hardware's expectation. This probably has some miniscule + * power cost, as TSOD polls will take 9 extra cycles. + */ + cmd |= SMBCMD_TRIGGER; + pci_write_config_dword(priv->pci_dev, SMBCMD(chan), cmd); + + if (!imc_wait_not_busy(priv, chan, &stat)) { + /* Timeout. TODO: Reset the controller? */ + ret = -ETIMEDOUT; + dev_dbg(&priv->pci_dev->dev, "controller is wedged\n"); + goto out_release; + } + + /* + * Be paranoid: try to detect races. This will only detect races + * against BIOS, not against hardware. (I've never seen this happen.) + */ + pci_read_config_dword(priv->pci_dev, SMBCMD(chan), &final_cmd); + pci_read_config_dword(priv->pci_dev, SMBCNTL(chan), &final_cntl); + if (((cmd ^ final_cmd) & SMBCMD_OUR_BITS) || + ((cntl ^ final_cntl) & SMBCNTL_OUR_BITS)) { + WARN(1, "iMC SMBUS raced against firmware"); + dev_err(&priv->pci_dev->dev, + "Access to channel %d raced: cmd 0x%08X->0x%08X, cntl 0x%08X->0x%08X\n", + chan, cmd, final_cmd, cntl, final_cntl); + atomic_set(&imc_raced, 1); + ret = -EIO; + goto out_release; + } + + if (stat & SMBSTAT_SBE) { + /* + * Clear the error to re-enable TSOD polling. The docs say + * that, as long as SBE is set, TSOD polling won't happen. + * The docs also say that writing zero to this bit (which is + * the only writable bit in the whole register) will clear + * the error. Empirically, writing 0 does not clear SBE, but + * it's probably still good to do the write in compliance with + * the spec. (TSOD polling still happens and seems to + * clear SBE on its own.) + */ + pci_write_config_dword(priv->pci_dev, SMBSTAT(chan), 0); + ret = -ENXIO; + goto out_release; + } + + if (read_write == I2C_SMBUS_READ) { + if (!(stat & SMBSTAT_RDO)) { + dev_dbg(&priv->pci_dev->dev, + "Unexpected read status 0x%08X\n", stat); + ret = -EIO; + goto out_release; + } + + /* + * The iMC SMBUS controller thinks of SMBUS words as + * being big-endian (MSB first). Linux treats them as + * little-endian, so we need to swap them. + * + * Note: the controller will often (always?) set WOD + * here. This is probably a hardware bug. + */ + if (size == I2C_SMBUS_WORD_DATA) + data->word = swab16(stat & SMBSTAT_RDATA_MASK); + else + data->byte = stat & 0xFF; + } else { + /* + * Note: the controller will often (always?) set RDO here. + * This is probably a hardware bug. + */ + if (!(stat & SMBSTAT_WOD)) { + dev_dbg(&priv->pci_dev->dev, + "Unexpected write status 0x%08X\n", stat); + ret = -EIO; + } + } + +out_release: + imc_channel_release(priv, chan); + +out_unlock: + mutex_unlock(&ch->mutex); + + return ret; +} + +static const struct i2c_algorithm imc_smbus_algorithm = { + .smbus_xfer = imc_smbus_xfer, + .functionality = imc_func, +}; + +static int imc_init_channel(struct imc_priv *priv, int i, int socket) +{ + int err; + u32 val; + struct imc_channel *ch = &priv->channels[i]; + + /* + * With CLTT enabled, the hardware won't let us turn + * off TSOD polling. The device is completely useless + * when this happens (at least without help from Intel), + * but we can at least minimize confusion. + */ + if (!imc_channel_can_claim(priv, i)) { + dev_warn(&priv->pci_dev->dev, + "iMC channel %d: we cannot control the HW. Is CLTT on?\n", + i); + return -EBUSY; + } + + i2c_set_adapdata(&ch->adapter, priv); + ch->adapter.owner = THIS_MODULE; + ch->adapter.algo = &imc_smbus_algorithm; + ch->adapter.dev.parent = &priv->pci_dev->dev; + + pci_read_config_dword(priv->pci_dev, SMBCNTL(i), &val); + ch->can_write = !(val & SMBCNTL_DIS_WRT); + + mutex_init(&ch->mutex); + + snprintf(ch->adapter.name, sizeof(ch->adapter.name), + "iMC socket %d channel %d", socket, i); + err = i2c_add_adapter(&ch->adapter); + if (err) { + mutex_destroy(&ch->mutex); + return err; + } + + i2c_scan_dimm_bus(&ch->adapter); + + return 0; +} + +static void imc_free_channel(struct imc_priv *priv, int i) +{ + struct imc_channel *ch = &priv->channels[i]; + + i2c_del_adapter(&ch->adapter); + mutex_destroy(&ch->mutex); +} + +static struct pci_dev *imc_get_related_device(struct pci_bus *bus, + unsigned int devfn, u16 devid) +{ + struct pci_dev *dev = pci_get_slot(bus, devfn); + + if (!dev) + return NULL; + if (dev->vendor != PCI_VENDOR_ID_INTEL || dev->device != devid) { + pci_dev_put(dev); + return NULL; + } + return dev; +} + +static int imc_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int i, j, err; + struct imc_priv *priv; + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->pci_dev = dev; + + pci_set_drvdata(dev, priv); + + for (i = 0; i < 1; i++) { + err = imc_init_channel(priv, i, 0); + if (err) + goto exit_free_channels; + printk(KERN_INFO "IMC: Create IMC SMBus OK.\n"); + } + + return 0; + +exit_free_channels: + printk(KERN_INFO "IMC: Free chennel I2C.\n"); + for (j = 0; j < i; j++) + imc_free_channel(priv, j); + return err; +} + +static void imc_remove(struct pci_dev *dev) +{ + int i; + struct imc_priv *priv = pci_get_drvdata(dev); + + for (i = 0; i < 1; i++) + imc_free_channel(priv, i); +} + +static int imc_suspend(struct pci_dev *dev, pm_message_t mesg) +{ + int i; + struct imc_priv *priv = pci_get_drvdata(dev); + + /* BIOS is in charge. We should finish any pending transaction */ + for (i = 0; i < 1; i++) { + mutex_lock(&priv->channels[i].mutex); + priv->channels[i].suspended = true; + mutex_unlock(&priv->channels[i].mutex); + } + + return 0; +} + +static int imc_resume(struct pci_dev *dev) +{ + int i; + struct imc_priv *priv = pci_get_drvdata(dev); + + for (i = 0; i < 1; i++) { + mutex_lock(&priv->channels[i].mutex); + priv->channels[i].suspended = false; + mutex_unlock(&priv->channels[i].mutex); + } + + return 0; +} + +static const struct pci_device_id imc_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TA) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, imc_ids); + +static struct pci_driver imc_pci_driver = { + .name = "imc_smbus", + .id_table = imc_ids, + .probe = imc_probe, + .remove = imc_remove, + .suspend = imc_suspend, + .resume = imc_resume, +}; + +static int __init i2c_imc_init(void) +{ + if (!allow_unsafe_access) + return -ENODEV; + + pr_warn("using this driver is dangerous unless your firmware is specifically designed for it; use at your own risk\n"); + return pci_register_driver(&imc_pci_driver); +} +module_init(i2c_imc_init); + +static void __exit i2c_imc_exit(void) +{ + pci_unregister_driver(&imc_pci_driver); +} +module_exit(i2c_imc_exit); + +module_param(allow_unsafe_access, bool, 0400); +MODULE_PARM_DESC(allow_unsafe_access, "enable i2c_imc despite potential races against BIOS/hardware bus access"); + +MODULE_AUTHOR("Andrew Lutomirski "); +MODULE_DESCRIPTION("iMC SMBus driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/i2c_dev_sysfs.c b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/i2c_dev_sysfs.c new file mode 100644 index 00000000000..a2e32c8e662 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/i2c_dev_sysfs.c @@ -0,0 +1,357 @@ +/* + * i2c_dev_sysfs.c - The i2c device sysfs library + * + * Copyright 2015-present Facebook. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +//#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c_dev_sysfs.h" + +#ifdef DEBUG + +#define PP_DEBUG(fmt, ...) do { \ + printk(KERN_DEBUG "%s:%d " fmt "\n", \ + __FUNCTION__, __LINE__, ##__VA_ARGS__); \ +} while (0) + +#else /* !DEBUG */ + +#define PP_DEBUG(fmt, ...) + +#endif + +ssize_t i2c_dev_show_label(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr); + const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr; + + if (dev_attr->ida_help) { + return sprintf(buf, "%s\n", dev_attr->ida_help); + } + return sprintf(buf, "%s\n", dev_attr->ida_name); +} +EXPORT_SYMBOL_GPL(i2c_dev_show_label); + +int i2c_dev_read_byte(struct device *dev, + struct device_attribute *attr) +{ + struct i2c_client *client = to_i2c_client(dev); + i2c_dev_data_st *data = i2c_get_clientdata(client); + i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr); + const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr; + int val; + int val_mask; + + val_mask = ~(((-1) >> (dev_attr->ida_n_bits)) << (dev_attr->ida_n_bits)); + + mutex_lock(&data->idd_lock); + + val = i2c_smbus_read_byte_data(client, dev_attr->ida_reg); + + mutex_unlock(&data->idd_lock); + + if (val < 0) { + /* error case */ + return val; + } + + val = (val >> dev_attr->ida_bit_offset) & val_mask; + return val; +} +EXPORT_SYMBOL_GPL(i2c_dev_read_byte); + +int i2c_dev_read_nbytes(struct device *dev, + struct device_attribute *attr, + uint8_t values[], + int nbytes) +{ + struct i2c_client *client = to_i2c_client(dev); + i2c_dev_data_st *data = i2c_get_clientdata(client); + i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr); + const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr; + int i; + int ret_val; + + mutex_lock(&data->idd_lock); + for (i = 0; i < nbytes; ++i) { + ret_val = i2c_smbus_read_byte_data(client, dev_attr->ida_reg + i); + if (ret_val < 0) { + mutex_unlock(&data->idd_lock); + return ret_val; + } + if (ret_val > 255) { + mutex_unlock(&data->idd_lock); + return EFAULT; + } + values[i] = ret_val; + } + mutex_unlock(&data->idd_lock); + return nbytes; +} +EXPORT_SYMBOL_GPL(i2c_dev_read_nbytes); + +int i2c_dev_read_word_littleendian(struct device *dev, + struct device_attribute *attr) +{ + uint8_t values[2]; + int ret_val; + + ret_val = i2c_dev_read_nbytes(dev, attr, values, 2); + if (ret_val < 0) { + return ret_val; + } + // values[0] : LSB + // values[1] : MSB + return ((values[1] << 8) + values[0]); +} +EXPORT_SYMBOL_GPL(i2c_dev_read_word_littleendian); + +int i2c_dev_read_word_bigendian(struct device *dev, + struct device_attribute *attr) +{ + uint8_t values[2]; + int ret_val; + + ret_val = i2c_dev_read_nbytes(dev, attr, values, 2); + if (ret_val < 0) { + return ret_val; + } + // values[0] : MSB + // values[1] : LSB + return ((values[0] << 8) + values[1]); +} +EXPORT_SYMBOL_GPL(i2c_dev_read_word_bigendian); + +ssize_t i2c_dev_show_ascii(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr); + const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr; + int nbytes, ret_val; + + nbytes = (dev_attr->ida_n_bits) / 8; + ret_val = i2c_dev_read_nbytes(dev, attr, buf, nbytes); + if (ret_val < 0) { + return ret_val; + } + //buf[] : ascii data + buf[nbytes] = '\n'; + return (nbytes + 1); +} +EXPORT_SYMBOL_GPL(i2c_dev_show_ascii); + +static ssize_t i2c_dev_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + i2c_dev_data_st *data = i2c_get_clientdata(client); + i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr); + const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr; + int val, val_mask, reg_val; + + if (!dev_attr->ida_show) { + return -EOPNOTSUPP; + } + + if (dev_attr->ida_show != I2C_DEV_ATTR_SHOW_DEFAULT) { + return dev_attr->ida_show(dev, attr, buf); + } + + val_mask = (1 << (dev_attr->ida_n_bits)) - 1; + + mutex_lock(&data->idd_lock); + + /* default handling */ + reg_val = i2c_smbus_read_byte_data(client, dev_attr->ida_reg); + + mutex_unlock(&data->idd_lock); + + if (reg_val < 0) { + /* error case */ + return reg_val; + } + + val = (reg_val >> dev_attr->ida_bit_offset) & val_mask; + + return scnprintf(buf, PAGE_SIZE, + "%#x\n\nNote:\n%s\n" + "Bit[%d:%d] @ register %#x, register value %#x\n", + val, (dev_attr->ida_help) ? dev_attr->ida_help : "", + dev_attr->ida_bit_offset + dev_attr->ida_n_bits - 1, + dev_attr->ida_bit_offset, dev_attr->ida_reg, reg_val); +} + +static ssize_t i2c_dev_sysfs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + i2c_dev_data_st *data = i2c_get_clientdata(client); + i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr); + const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr; + int val; + int req_val; + int req_val_mask; + + if (!dev_attr->ida_store) { + return -EOPNOTSUPP; + } + + if (dev_attr->ida_store != I2C_DEV_ATTR_STORE_DEFAULT) { + return dev_attr->ida_store(dev, attr, buf, count); + } + + /* parse the buffer */ + if (sscanf(buf, "%i", &req_val) <= 0) { + return -EINVAL; + } + req_val_mask = ~(((-1) >> (dev_attr->ida_n_bits)) << (dev_attr->ida_n_bits)); + req_val &= req_val_mask; + + mutex_lock(&data->idd_lock); + + /* default handling, first read back the current value */ + val = i2c_smbus_read_byte_data(client, dev_attr->ida_reg); + + if (val < 0) { + /* fail to read */ + goto unlock_out; + } + + /* mask out all bits for the value requested */ + val &= ~(req_val_mask << dev_attr->ida_bit_offset); + val |= req_val << dev_attr->ida_bit_offset; + + val = i2c_smbus_write_byte_data(client, dev_attr->ida_reg, val); + +unlock_out: + mutex_unlock(&data->idd_lock); + + if (val < 0) { + /* error case */ + return val; + } + + return count; +} + +void i2c_dev_sysfs_data_clean(struct i2c_client *client, i2c_dev_data_st *data) +{ + if (!data) { + return; + } + if (data->idd_hwmon_dev) { + hwmon_device_unregister(data->idd_hwmon_dev); + } + if (data->idd_attr_group.attrs) { + sysfs_remove_group(&client->dev.kobj, &data->idd_attr_group); + kfree(data->idd_attr_group.attrs); + } + if (data->idd_attrs) { + kfree(data->idd_attrs); + } + memset(data, 0, sizeof(*data)); +} +EXPORT_SYMBOL_GPL(i2c_dev_sysfs_data_clean); + +int i2c_dev_sysfs_data_init(struct i2c_client *client, + i2c_dev_data_st *data, + const i2c_dev_attr_st *dev_attrs, + int n_attrs) +{ + int i; + int err; + i2c_sysfs_attr_st *cur_attr; + const i2c_dev_attr_st *cur_dev_attr; + struct attribute **cur_grp_attr; + + memset(data, 0, sizeof(*data)); + + i2c_set_clientdata(client, data); + mutex_init(&data->idd_lock); + + /* allocate all attribute */ + data->idd_attrs = kzalloc(sizeof(*data->idd_attrs) * n_attrs, GFP_KERNEL); + /* allocate the attribute group. +1 for the last NULL */ + data->idd_attr_group.attrs + = kzalloc(sizeof(*data->idd_attr_group.attrs) * (n_attrs + 1), GFP_KERNEL); + if (!data->idd_attrs || !data->idd_attr_group.attrs) { + err = -ENOMEM; + goto exit_cleanup; + } + PP_DEBUG("Allocated %u attributes", n_attrs); + + for (i = 0, + cur_attr = &data->idd_attrs[0], + cur_grp_attr = &data->idd_attr_group.attrs[0], + cur_dev_attr = dev_attrs; + i < n_attrs; + i++, cur_attr++, cur_grp_attr++, cur_dev_attr++) { + + mode_t mode = S_IRUGO; + if (cur_dev_attr->ida_store) { + mode |= S_IWUSR; + } + cur_attr->isa_dev_attr.attr.name = cur_dev_attr->ida_name; + cur_attr->isa_dev_attr.attr.mode = mode; + cur_attr->isa_dev_attr.show = i2c_dev_sysfs_show; + cur_attr->isa_dev_attr.store = i2c_dev_sysfs_store; + *cur_grp_attr = &cur_attr->isa_dev_attr.attr; + cur_attr->isa_i2c_attr = cur_dev_attr; + PP_DEBUG("Created attribute \"%s\"", cur_attr->isa_dev_attr.attr.name); + } + + /* Register sysfs hooks */ + if ((err = sysfs_create_group(&client->dev.kobj, &data->idd_attr_group))) { + goto exit_cleanup; + } + + data->idd_hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->idd_hwmon_dev)) { + err = PTR_ERR(data->idd_hwmon_dev); + data->idd_hwmon_dev = NULL; + goto exit_cleanup; + } + + PP_DEBUG("i2c device sysfs init data done"); + return 0; + +exit_cleanup: + i2c_dev_sysfs_data_clean(client, data); + return err; +} +EXPORT_SYMBOL_GPL(i2c_dev_sysfs_data_init); + + +MODULE_AUTHOR("Tian Fang "); +MODULE_DESCRIPTION("i2c device sysfs attribute library"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/i2c_dev_sysfs.h b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/i2c_dev_sysfs.h new file mode 100644 index 00000000000..4c69b827341 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/i2c_dev_sysfs.h @@ -0,0 +1,84 @@ +/* + * i2c_dev_sysfs.h - The i2c device sysfs library header + * + * Copyright 2015-present Facebook. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef I2C_DEV_SYSFS_H +#define I2C_DEV_SYSFS_H + +#include +#include + +typedef ssize_t (*i2c_dev_attr_show_fn)(struct device *dev, + struct device_attribute *attr, + char *buf); +typedef ssize_t (*i2c_dev_attr_store_fn)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +#define I2C_DEV_ATTR_SHOW_DEFAULT (i2c_dev_attr_show_fn)(1) +#define I2C_DEV_ATTR_STORE_DEFAULT (i2c_dev_attr_store_fn)(1) + +typedef struct i2c_dev_attr_st_ { + const char *ida_name; + const char *ida_help; + i2c_dev_attr_show_fn ida_show; + i2c_dev_attr_store_fn ida_store; + int ida_reg; + int ida_bit_offset; + int ida_n_bits; +} i2c_dev_attr_st; + +typedef struct i2c_sysfs_attr_st_ { + struct device_attribute isa_dev_attr; + const i2c_dev_attr_st *isa_i2c_attr; +} i2c_sysfs_attr_st; + +#define TO_I2C_SYSFS_ATTR(_attr) \ + container_of(_attr, i2c_sysfs_attr_st, isa_dev_attr) + +typedef struct i2c_dev_data_st_ { + struct device *idd_hwmon_dev; + struct mutex idd_lock; + i2c_sysfs_attr_st *idd_attrs; + struct attribute_group idd_attr_group; +} i2c_dev_data_st; + +int i2c_dev_sysfs_data_init(struct i2c_client *client, + i2c_dev_data_st *data, + const i2c_dev_attr_st *dev_attrs, + int n_attrs); +void i2c_dev_sysfs_data_clean(struct i2c_client *client, i2c_dev_data_st *data); +int i2c_dev_read_byte(struct device *dev, + struct device_attribute *attr); +int i2c_dev_read_nbytes(struct device *dev, + struct device_attribute *attr, + uint8_t values[], + int nbytes); +int i2c_dev_read_word_littleendian(struct device *dev, + struct device_attribute *attr); +int i2c_dev_read_word_bigendian(struct device *dev, + struct device_attribute *attr); +ssize_t i2c_dev_show_label(struct device *dev, + struct device_attribute *attr, + char *buf); +ssize_t i2c_dev_show_ascii(struct device *dev, + struct device_attribute *attr, + char *buf); + +#endif diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/mc24lc64t.c b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/mc24lc64t.c new file mode 100644 index 00000000000..ae79770a4d8 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/mc24lc64t.c @@ -0,0 +1,173 @@ +/* + * mc24lc64t.c - driver for Microchip 24LC64T + * + * Copyright (C) 2017 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define EEPROM_SIZE 8192 //mc24lt64t eeprom size in bytes. + +struct mc24lc64t_data { + struct mutex update_lock; +}; + +static ssize_t mc24lc64t_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = kobj_to_i2c_client(kobj); + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + unsigned long timeout, read_time, i = 0; + int status; + + mutex_lock(&drvdata->update_lock); + + if (i2c_smbus_write_byte_data(client, off>>8, off)) + { + status = -EIO; + goto exit; + } + + msleep(1); + +begin: + + if (i < count) + { + timeout = jiffies + msecs_to_jiffies(25); /* 25 mS timeout*/ + do { + read_time = jiffies; + + status = i2c_smbus_read_byte(client); + if (status >= 0) + { + buf[i++] = status; + goto begin; + } + } while (time_before(read_time, timeout)); + + status = -ETIMEDOUT; + goto exit; + } + + status = count; + +exit: + mutex_unlock(&drvdata->update_lock); + + return status; +} + +static ssize_t mc24lc64t_write (struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count){ + + struct i2c_client *client = kobj_to_i2c_client(kobj); + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + unsigned long timeout, write_time, i = 0; + int status; + u16 value; + + mutex_lock(&drvdata->update_lock); + +begin: + if (i < count){ + timeout = jiffies + msecs_to_jiffies(25); /* 25 mS timeout*/ + value = (buf[i] << 8)| off; + do { + write_time = jiffies; + status = i2c_smbus_write_word_data(client, off>>8, value); + if (status >= 0) + { + // increase offset + off++; + // increase buffer index + i++; + goto begin; + } + } while (time_before(write_time, timeout)); + status = -ETIMEDOUT; + goto exit; + } + status = count; + +exit: + mutex_unlock(&drvdata->update_lock); + return status; +} + +static struct bin_attribute mc24lc64t_bit_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO | S_IWUGO, + }, + .size = EEPROM_SIZE, + .read = mc24lc64t_read, + .write = mc24lc64t_write, +}; + +static int mc24lc64t_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct mc24lc64t_data *drvdata; + int err; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA + | I2C_FUNC_SMBUS_READ_BYTE)) + return -EPFNOSUPPORT; + + if (!(drvdata = devm_kzalloc(&client->dev, + sizeof(struct mc24lc64t_data), GFP_KERNEL))) + return -ENOMEM; + + i2c_set_clientdata(client, drvdata); + mutex_init(&drvdata->update_lock); + + err = sysfs_create_bin_file(&client->dev.kobj, &mc24lc64t_bit_attr); + + return err; +} + +static int mc24lc64t_remove(struct i2c_client *client) +{ + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + sysfs_remove_bin_file(&client->dev.kobj, &mc24lc64t_bit_attr); + + return 0; +} + +static const struct i2c_device_id mc24lc64t_id[] = { + { "24lc64t", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mc24lc64t_id); + +static struct i2c_driver mc24lc64t_driver = { + .driver = { + .name = "mc24lc64t", + .owner = THIS_MODULE, + }, + .probe = mc24lc64t_probe, + .remove = mc24lc64t_remove, + .id_table = mc24lc64t_id, +}; + +module_i2c_driver(mc24lc64t_driver); + +MODULE_AUTHOR("Abhisit Sangjan "); +MODULE_DESCRIPTION("Microchip 24LC64T Driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/pmbus.h b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/pmbus.h new file mode 100644 index 00000000000..c0df0d7daa1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/pmbus.h @@ -0,0 +1,426 @@ +/* + * pmbus.h - Common defines and structures for PMBus devices + * + * Copyright (c) 2010, 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PMBUS_H +#define PMBUS_H + +#include +#include + +/* + * Registers + */ +enum pmbus_regs { + PMBUS_PAGE = 0x00, + PMBUS_OPERATION = 0x01, + PMBUS_ON_OFF_CONFIG = 0x02, + PMBUS_CLEAR_FAULTS = 0x03, + PMBUS_PHASE = 0x04, + + PMBUS_CAPABILITY = 0x19, + PMBUS_QUERY = 0x1A, + + PMBUS_VOUT_MODE = 0x20, + PMBUS_VOUT_COMMAND = 0x21, + PMBUS_VOUT_TRIM = 0x22, + PMBUS_VOUT_CAL_OFFSET = 0x23, + PMBUS_VOUT_MAX = 0x24, + PMBUS_VOUT_MARGIN_HIGH = 0x25, + PMBUS_VOUT_MARGIN_LOW = 0x26, + PMBUS_VOUT_TRANSITION_RATE = 0x27, + PMBUS_VOUT_DROOP = 0x28, + PMBUS_VOUT_SCALE_LOOP = 0x29, + PMBUS_VOUT_SCALE_MONITOR = 0x2A, + + PMBUS_COEFFICIENTS = 0x30, + PMBUS_POUT_MAX = 0x31, + + PMBUS_FAN_CONFIG_12 = 0x3A, + PMBUS_FAN_COMMAND_1 = 0x3B, + PMBUS_FAN_COMMAND_2 = 0x3C, + PMBUS_FAN_CONFIG_34 = 0x3D, + PMBUS_FAN_COMMAND_3 = 0x3E, + PMBUS_FAN_COMMAND_4 = 0x3F, + + PMBUS_VOUT_OV_FAULT_LIMIT = 0x40, + PMBUS_VOUT_OV_FAULT_RESPONSE = 0x41, + PMBUS_VOUT_OV_WARN_LIMIT = 0x42, + PMBUS_VOUT_UV_WARN_LIMIT = 0x43, + PMBUS_VOUT_UV_FAULT_LIMIT = 0x44, + PMBUS_VOUT_UV_FAULT_RESPONSE = 0x45, + PMBUS_IOUT_OC_FAULT_LIMIT = 0x46, + PMBUS_IOUT_OC_FAULT_RESPONSE = 0x47, + PMBUS_IOUT_OC_LV_FAULT_LIMIT = 0x48, + PMBUS_IOUT_OC_LV_FAULT_RESPONSE = 0x49, + PMBUS_IOUT_OC_WARN_LIMIT = 0x4A, + PMBUS_IOUT_UC_FAULT_LIMIT = 0x4B, + PMBUS_IOUT_UC_FAULT_RESPONSE = 0x4C, + + PMBUS_OT_FAULT_LIMIT = 0x4F, + PMBUS_OT_FAULT_RESPONSE = 0x50, + PMBUS_OT_WARN_LIMIT = 0x51, + PMBUS_UT_WARN_LIMIT = 0x52, + PMBUS_UT_FAULT_LIMIT = 0x53, + PMBUS_UT_FAULT_RESPONSE = 0x54, + PMBUS_VIN_OV_FAULT_LIMIT = 0x55, + PMBUS_VIN_OV_FAULT_RESPONSE = 0x56, + PMBUS_VIN_OV_WARN_LIMIT = 0x57, + PMBUS_VIN_UV_WARN_LIMIT = 0x58, + PMBUS_VIN_UV_FAULT_LIMIT = 0x59, + + PMBUS_IIN_OC_FAULT_LIMIT = 0x5B, + PMBUS_IIN_OC_WARN_LIMIT = 0x5D, + + PMBUS_POUT_OP_FAULT_LIMIT = 0x68, + PMBUS_POUT_OP_WARN_LIMIT = 0x6A, + PMBUS_PIN_OP_WARN_LIMIT = 0x6B, + + PMBUS_STATUS_BYTE = 0x78, + PMBUS_STATUS_WORD = 0x79, + PMBUS_STATUS_VOUT = 0x7A, + PMBUS_STATUS_IOUT = 0x7B, + PMBUS_STATUS_INPUT = 0x7C, + PMBUS_STATUS_TEMPERATURE = 0x7D, + PMBUS_STATUS_CML = 0x7E, + PMBUS_STATUS_OTHER = 0x7F, + PMBUS_STATUS_MFR_SPECIFIC = 0x80, + PMBUS_STATUS_FAN_12 = 0x81, + PMBUS_STATUS_FAN_34 = 0x82, + + PMBUS_READ_VIN = 0x88, + PMBUS_READ_IIN = 0x89, + PMBUS_READ_VCAP = 0x8A, + PMBUS_READ_VOUT = 0x8B, + PMBUS_READ_IOUT = 0x8C, + PMBUS_READ_TEMPERATURE_1 = 0x8D, + PMBUS_READ_TEMPERATURE_2 = 0x8E, + PMBUS_READ_TEMPERATURE_3 = 0x8F, + PMBUS_READ_FAN_SPEED_1 = 0x90, + PMBUS_READ_FAN_SPEED_2 = 0x91, + PMBUS_READ_FAN_SPEED_3 = 0x92, + PMBUS_READ_FAN_SPEED_4 = 0x93, + PMBUS_READ_DUTY_CYCLE = 0x94, + PMBUS_READ_FREQUENCY = 0x95, + PMBUS_READ_POUT = 0x96, + PMBUS_READ_PIN = 0x97, + + PMBUS_REVISION = 0x98, + PMBUS_MFR_ID = 0x99, + PMBUS_MFR_MODEL = 0x9A, + PMBUS_MFR_REVISION = 0x9B, + PMBUS_MFR_LOCATION = 0x9C, + PMBUS_MFR_DATE = 0x9D, + PMBUS_MFR_SERIAL = 0x9E, + + /* + * Virtual registers. + * Useful to support attributes which are not supported by standard PMBus + * registers but exist as manufacturer specific registers on individual chips. + * Must be mapped to real registers in device specific code. + * + * Semantics: + * Virtual registers are all word size. + * READ registers are read-only; writes are either ignored or return an error. + * RESET registers are read/write. Reading reset registers returns zero + * (used for detection), writing any value causes the associated history to be + * reset. + * Virtual registers have to be handled in device specific driver code. Chip + * driver code returns non-negative register values if a virtual register is + * supported, or a negative error code if not. The chip driver may return + * -ENODATA or any other error code in this case, though an error code other + * than -ENODATA is handled more efficiently and thus preferred. Either case, + * the calling PMBus core code will abort if the chip driver returns an error + * code when reading or writing virtual registers. + */ + PMBUS_VIRT_BASE = 0x100, + PMBUS_VIRT_READ_TEMP_AVG, + PMBUS_VIRT_READ_TEMP_MIN, + PMBUS_VIRT_READ_TEMP_MAX, + PMBUS_VIRT_RESET_TEMP_HISTORY, + PMBUS_VIRT_READ_VIN_AVG, + PMBUS_VIRT_READ_VIN_MIN, + PMBUS_VIRT_READ_VIN_MAX, + PMBUS_VIRT_RESET_VIN_HISTORY, + PMBUS_VIRT_READ_IIN_AVG, + PMBUS_VIRT_READ_IIN_MIN, + PMBUS_VIRT_READ_IIN_MAX, + PMBUS_VIRT_RESET_IIN_HISTORY, + PMBUS_VIRT_READ_PIN_AVG, + PMBUS_VIRT_READ_PIN_MIN, + PMBUS_VIRT_READ_PIN_MAX, + PMBUS_VIRT_RESET_PIN_HISTORY, + PMBUS_VIRT_READ_POUT_AVG, + PMBUS_VIRT_READ_POUT_MIN, + PMBUS_VIRT_READ_POUT_MAX, + PMBUS_VIRT_RESET_POUT_HISTORY, + PMBUS_VIRT_READ_VOUT_AVG, + PMBUS_VIRT_READ_VOUT_MIN, + PMBUS_VIRT_READ_VOUT_MAX, + PMBUS_VIRT_RESET_VOUT_HISTORY, + PMBUS_VIRT_READ_IOUT_AVG, + PMBUS_VIRT_READ_IOUT_MIN, + PMBUS_VIRT_READ_IOUT_MAX, + PMBUS_VIRT_RESET_IOUT_HISTORY, + PMBUS_VIRT_READ_TEMP2_AVG, + PMBUS_VIRT_READ_TEMP2_MIN, + PMBUS_VIRT_READ_TEMP2_MAX, + PMBUS_VIRT_RESET_TEMP2_HISTORY, + + PMBUS_VIRT_READ_VMON, + PMBUS_VIRT_VMON_UV_WARN_LIMIT, + PMBUS_VIRT_VMON_OV_WARN_LIMIT, + PMBUS_VIRT_VMON_UV_FAULT_LIMIT, + PMBUS_VIRT_VMON_OV_FAULT_LIMIT, + PMBUS_VIRT_STATUS_VMON, +}; + +/* + * OPERATION + */ +#define PB_OPERATION_CONTROL_ON BIT(7) + +/* + * CAPABILITY + */ +#define PB_CAPABILITY_SMBALERT BIT(4) +#define PB_CAPABILITY_ERROR_CHECK BIT(7) + +/* + * VOUT_MODE + */ +#define PB_VOUT_MODE_MODE_MASK 0xe0 +#define PB_VOUT_MODE_PARAM_MASK 0x1f + +#define PB_VOUT_MODE_LINEAR 0x00 +#define PB_VOUT_MODE_VID 0x20 +#define PB_VOUT_MODE_DIRECT 0x40 + +/* + * Fan configuration + */ +#define PB_FAN_2_PULSE_MASK (BIT(0) | BIT(1)) +#define PB_FAN_2_RPM BIT(2) +#define PB_FAN_2_INSTALLED BIT(3) +#define PB_FAN_1_PULSE_MASK (BIT(4) | BIT(5)) +#define PB_FAN_1_RPM BIT(6) +#define PB_FAN_1_INSTALLED BIT(7) + +/* + * STATUS_BYTE, STATUS_WORD (lower) + */ +#define PB_STATUS_NONE_ABOVE BIT(0) +#define PB_STATUS_CML BIT(1) +#define PB_STATUS_TEMPERATURE BIT(2) +#define PB_STATUS_VIN_UV BIT(3) +#define PB_STATUS_IOUT_OC BIT(4) +#define PB_STATUS_VOUT_OV BIT(5) +#define PB_STATUS_OFF BIT(6) +#define PB_STATUS_BUSY BIT(7) + +/* + * STATUS_WORD (upper) + */ +#define PB_STATUS_UNKNOWN BIT(8) +#define PB_STATUS_OTHER BIT(9) +#define PB_STATUS_FANS BIT(10) +#define PB_STATUS_POWER_GOOD_N BIT(11) +#define PB_STATUS_WORD_MFR BIT(12) +#define PB_STATUS_INPUT BIT(13) +#define PB_STATUS_IOUT_POUT BIT(14) +#define PB_STATUS_VOUT BIT(15) + +/* + * STATUS_IOUT + */ +#define PB_POUT_OP_WARNING BIT(0) +#define PB_POUT_OP_FAULT BIT(1) +#define PB_POWER_LIMITING BIT(2) +#define PB_CURRENT_SHARE_FAULT BIT(3) +#define PB_IOUT_UC_FAULT BIT(4) +#define PB_IOUT_OC_WARNING BIT(5) +#define PB_IOUT_OC_LV_FAULT BIT(6) +#define PB_IOUT_OC_FAULT BIT(7) + +/* + * STATUS_VOUT, STATUS_INPUT + */ +#define PB_VOLTAGE_UV_FAULT BIT(4) +#define PB_VOLTAGE_UV_WARNING BIT(5) +#define PB_VOLTAGE_OV_WARNING BIT(6) +#define PB_VOLTAGE_OV_FAULT BIT(7) + +/* + * STATUS_INPUT + */ +#define PB_PIN_OP_WARNING BIT(0) +#define PB_IIN_OC_WARNING BIT(1) +#define PB_IIN_OC_FAULT BIT(2) + +/* + * STATUS_TEMPERATURE + */ +#define PB_TEMP_UT_FAULT BIT(4) +#define PB_TEMP_UT_WARNING BIT(5) +#define PB_TEMP_OT_WARNING BIT(6) +#define PB_TEMP_OT_FAULT BIT(7) + +/* + * STATUS_FAN + */ +#define PB_FAN_AIRFLOW_WARNING BIT(0) +#define PB_FAN_AIRFLOW_FAULT BIT(1) +#define PB_FAN_FAN2_SPEED_OVERRIDE BIT(2) +#define PB_FAN_FAN1_SPEED_OVERRIDE BIT(3) +#define PB_FAN_FAN2_WARNING BIT(4) +#define PB_FAN_FAN1_WARNING BIT(5) +#define PB_FAN_FAN2_FAULT BIT(6) +#define PB_FAN_FAN1_FAULT BIT(7) + +/* + * CML_FAULT_STATUS + */ +#define PB_CML_FAULT_OTHER_MEM_LOGIC BIT(0) +#define PB_CML_FAULT_OTHER_COMM BIT(1) +#define PB_CML_FAULT_PROCESSOR BIT(3) +#define PB_CML_FAULT_MEMORY BIT(4) +#define PB_CML_FAULT_PACKET_ERROR BIT(5) +#define PB_CML_FAULT_INVALID_DATA BIT(6) +#define PB_CML_FAULT_INVALID_COMMAND BIT(7) + +enum pmbus_sensor_classes { + PSC_VOLTAGE_IN = 0, + PSC_VOLTAGE_OUT, + PSC_CURRENT_IN, + PSC_CURRENT_OUT, + PSC_POWER, + PSC_TEMPERATURE, + PSC_FAN, + PSC_NUM_CLASSES /* Number of power sensor classes */ +}; + +#define PMBUS_PAGES 32 /* Per PMBus specification */ + +/* Functionality bit mask */ +#define PMBUS_HAVE_VIN BIT(0) +#define PMBUS_HAVE_VCAP BIT(1) +#define PMBUS_HAVE_VOUT BIT(2) +#define PMBUS_HAVE_IIN BIT(3) +#define PMBUS_HAVE_IOUT BIT(4) +#define PMBUS_HAVE_PIN BIT(5) +#define PMBUS_HAVE_POUT BIT(6) +#define PMBUS_HAVE_FAN12 BIT(7) +#define PMBUS_HAVE_FAN34 BIT(8) +#define PMBUS_HAVE_TEMP BIT(9) +#define PMBUS_HAVE_TEMP2 BIT(10) +#define PMBUS_HAVE_TEMP3 BIT(11) +#define PMBUS_HAVE_STATUS_VOUT BIT(12) +#define PMBUS_HAVE_STATUS_IOUT BIT(13) +#define PMBUS_HAVE_STATUS_INPUT BIT(14) +#define PMBUS_HAVE_STATUS_TEMP BIT(15) +#define PMBUS_HAVE_STATUS_FAN12 BIT(16) +#define PMBUS_HAVE_STATUS_FAN34 BIT(17) +#define PMBUS_HAVE_VMON BIT(18) +#define PMBUS_HAVE_STATUS_VMON BIT(19) + +enum pmbus_data_format { linear = 0, direct, vid }; +enum vrm_version { vr11 = 0, vr12 }; + +struct pmbus_driver_info { + int pages; /* Total number of pages */ + enum pmbus_data_format format[PSC_NUM_CLASSES]; + enum vrm_version vrm_version; + /* + * Support one set of coefficients for each sensor type + * Used for chips providing data in direct mode. + */ + int m[PSC_NUM_CLASSES]; /* mantissa for direct data format */ + int b[PSC_NUM_CLASSES]; /* offset */ + int R[PSC_NUM_CLASSES]; /* exponent */ + + u32 func[PMBUS_PAGES]; /* Functionality, per page */ + /* + * The following functions map manufacturing specific register values + * to PMBus standard register values. Specify only if mapping is + * necessary. + * Functions return the register value (read) or zero (write) if + * successful. A return value of -ENODATA indicates that there is no + * manufacturer specific register, but that a standard PMBus register + * may exist. Any other negative return value indicates that the + * register does not exist, and that no attempt should be made to read + * the standard register. + */ + int (*read_byte_data)(struct i2c_client *client, int page, int reg); + int (*read_word_data)(struct i2c_client *client, int page, int reg); + int (*write_word_data)(struct i2c_client *client, int page, int reg, + u16 word); + int (*write_byte)(struct i2c_client *client, int page, u8 value); + /* + * The identify function determines supported PMBus functionality. + * This function is only necessary if a chip driver supports multiple + * chips, and the chip functionality is not pre-determined. + */ + int (*identify)(struct i2c_client *client, + struct pmbus_driver_info *info); + + /* Regulator functionality, if supported by this chip driver. */ + int num_regulators; + const struct regulator_desc *reg_desc; +}; + +/* Regulator ops */ + +extern const struct regulator_ops pmbus_regulator_ops; + +/* Macro for filling in array of struct regulator_desc */ +#define PMBUS_REGULATOR(_name, _id) \ + [_id] = { \ + .name = (_name # _id), \ + .id = (_id), \ + .of_match = of_match_ptr(_name # _id), \ + .regulators_node = of_match_ptr("regulators"), \ + .ops = &pmbus_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +/* Function declarations */ + +void pmbus_wait(struct i2c_client *client); +void pmbus_update_wait(struct i2c_client *client); +void pmbus_clear_cache(struct i2c_client *client); +int pmbus_set_page(struct i2c_client *client, u8 page); +int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); +int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word); +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); +int pmbus_write_byte(struct i2c_client *client, int page, u8 value); +int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, + u8 value); +int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + u8 mask, u8 value); +void pmbus_clear_faults(struct i2c_client *client); +bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); +bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); +int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, + struct pmbus_driver_info *info); +int pmbus_do_remove(struct i2c_client *client); +const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client); + +#endif /* PMBUS_H */ \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/switchboard_fpga.c b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/switchboard_fpga.c new file mode 100644 index 00000000000..82c5dcc049c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/switchboard_fpga.c @@ -0,0 +1,2522 @@ +/* + * switchboard.c - Driver for questone2bd Switch FPGA/CPLD. + * + * Author: Pradchaya Phucharoen + * + * Copyright (C) 2018 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 + * \--questone2bd.switchboard + * |--FPGA + * |--CPLD1 + * |--CPLD2 + * \--SFF + * |--SFP[1..48] + * \--QSFP[1..8] + * + */ + +#ifndef TEST_MODE +#define MOD_VERSION "0.5.4" +#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 +#include +#include +#include + +static int majorNumber; + +#define CLASS_NAME "questone2bd_fpga" +#define DRIVER_NAME "questone2bd.switchboard" +#define FPGA_PCI_NAME "questone2bd_fpga_pci" +#define DEVICE_NAME "fwupgrade" + +static bool allow_unsafe_i2c_access; + +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 i2c_core_init(unsigned int master_bus, unsigned int freq_div,void __iomem *pci_bar); +static void i2c_core_deinit(unsigned int master_bus, void __iomem *pci_bar); +static int i2c_xcvr_access(u8 register_address, unsigned int portid, u8 *data, char rw); + +static int fpgafw_init(void); +static void fpgafw_exit(void); + +/* +======================================== +FPGA PCIe BAR 0 Registers +======================================== +Misc Control 0x00000000 – 0x000000FF +I2C_CH1 0x00000800 - 0x0000081C +I2C_CH2 0x00000820 - 0x0000083C +I2C_CH3 0x00000840 - 0x0000085C +I2C_CH4 0x00000860 - 0x0000087C +I2C_CH5 0x00000880 - 0x0000089C +I2C_CH6 0x000008A0 - 0x000008BC +I2C_CH7 0x000008C0 - 0x000008DC +I2C_CH8 0x000008E0 - 0x000008FC +I2C_CH9 0x00000900 - 0x0000091C +I2C_CH10 0x00000920 - 0x0000093C +I2C_CH11 0x00000940 - 0x0000095C +I2C_CH12 0x00000960 - 0x0000097C +I2C_CH13 0x00000980 - 0x0000099C +I2C_CH14 0x000009A0 - 0x000009BC +SPI Master 0x00000A00 - 0x00000BFC +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 BMC_I2C_SCRATCH 0x0020 +#define FPGA_SLAVE_CPLD_REST 0x0100 +#define FPGA_SWITCH_RESET_CTRL 0x0104 +#define FPAG_PRH_RESER_CTRL 0x0108 +#define FPGA_INT_STATUS 0x0200 +#define FPGA_INT_SRC_STATUS 0x0204 +#define FPGA_INT_FLAG 0x0208 +#define FPGA_INT_MASK 0x020c +#define FPGA_MISC_CTRL 0x0300 +#define FPGA_MISC_STATUS 0x0304 + +/* I2C_MASTER BASE ADDR */ +#define I2C_MASTER_FREQ_L 0x0800 +#define I2C_MASTER_FREQ_H 0x0804 +#define I2C_MASTER_CTRL 0x0808 +#define I2C_MASTER_DATA 0x080c +#define I2C_MASTER_CMD 0x0810 /* Write-Only Register */ +#define I2C_MASTER_STATUS 0x0810 /* Read-Only Register */ +#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_11 11 +#define I2C_MASTER_CH_12 12 +#define I2C_MASTER_CH_13 13 +#define I2C_MASTER_CH_14 14 + +#define I2C_MASTER_CH_TOTAL I2C_MASTER_CH_14 + +/* 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] RSVD +[5] MODSEL 5 +[4] RST 4 +[3:1] RSVD +[0] TXDIS 0 +*/ +#define CTRL_MODSEL 5 +#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 + + +/** + * Switchboard CPLD XCVR registers + */ + +/* PORT SEL REGISTER +[7:5] RSVD +[4:0] ID +*/ +#define I2C_XCVR_SEL 0x10 +#define I2C_SEL_ID 0 + +/* PORT CTRL REGISTER +[7:5] RSVD +[4] RST +[3:1] RSVD +[0] TXDIS/MODSEL +*/ +#define I2C_XCVR_CTRL 0x11 +#define I2C_CTRL_RST 4 +#define I2C_CTRL_MODSEL 0 +#define I2C_CTRL_TXDIS 0 + +/* PORT STATUS REGISTER +[7:5] RSVD +[4] PRESENT/ABS +[3:2] RSVD +[1] TXFAULT +[0] RXLOS/INT_N +*/ +#define I2C_XCVR_STAT 0x12 +#define I2C_STAT_PRESENT 4 +#define I2C_STAT_MODABS 4 +#define I2C_STAT_TXFAULT 1 +#define I2C_STAT_INT_N 0 +#define I2C_STAT_RXLOS 0 + +/* PORT INTRPT REGISTER +[7:5] RSVD +[4] PRESENT/ABS +[3:2] RSVD +[1] TXFAULT +[0] RXLOS/INT_N +*/ +#define I2C_XCVR_INRT 0x13 +#define I2C_INTR_PRESENT 4 +#define I2C_INTR_MODABS 4 +#define I2C_INTR_TXFAULT 1 +#define I2C_INTR_INT_N 0 +#define I2C_INTR_RXLOS 0 + +/* PORT INTR MASK REGISTER +[31:6] RSVD +[5] INT_N 5 +[4] PRESENT 4 +[3] RSVD +[2] RSVD +[1] RXLOS_INT 1 +[0] MODABS 0 +*/ +#define I2C_XCVR_MASK 0x14 +#define I2C_MASK_PRESENT 4 +#define I2C_MASK_MODABS 4 +#define I2C_MASK_TXFAULT 1 +#define I2C_MASK_INT_N 0 +#define I2C_MASK_RXLOS 0 + + +/* I2C master clock speed */ +// NOTE: Only I2C clock in normal mode is support here. +enum { + I2C_DIV_100K = 0x71, +}; + +/* I2C Master control register */ +enum { + I2C_CTRL_IEN = 6, + I2C_CTRL_EN +}; + +/* I2C Master command register */ +enum { + I2C_CMD_IACK = 0, + I2C_CMD_ACK = 3, + I2C_CMD_WR, + I2C_CMD_RD, + I2C_CMD_STO, + I2C_CMD_STA, +}; + +/* I2C Master status register */ +enum { + I2C_STAT_IF = 0, + I2C_STAT_TIP, + I2C_STAT_AL = 5, + I2C_STAT_BUSY, + I2C_STAT_RxACK, +}; + +/** + * + * 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_SFP_PORT 48 +#define VIRTUAL_I2C_QSFP_PORT 8 + +#define SFF_PORT_TOTAL VIRTUAL_I2C_QSFP_PORT + VIRTUAL_I2C_SFP_PORT + +#define VIRTUAL_I2C_BUS_OFFSET 10 +#define BB_CPLD_SLAVE_ADDR 0x0d +#define FAN_CPLD_SLAVE_ADDR 0x0d +#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 + +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]; +static int nack_retry[I2C_MASTER_CH_TOTAL]; +static int need_retry[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[] = { + /* SFP and QSFP front panel I2C */ + {I2C_MASTER_CH_11, 0x70, 0, SFP, "SFP1"}, {I2C_MASTER_CH_11, 0x70, 1, SFP, "SFP2"}, + {I2C_MASTER_CH_11, 0x70, 2, SFP, "SFP3"}, {I2C_MASTER_CH_11, 0x70, 3, SFP, "SFP4"}, + {I2C_MASTER_CH_11, 0x70, 4, SFP, "SFP5"}, {I2C_MASTER_CH_11, 0x70, 5, SFP, "SFP6"}, + {I2C_MASTER_CH_11, 0x70, 6, SFP, "SFP7"}, {I2C_MASTER_CH_11, 0x70, 7, SFP, "SFP8"}, + + {I2C_MASTER_CH_11, 0x71, 0, SFP, "SFP9"}, {I2C_MASTER_CH_11, 0x71, 1, SFP, "SFP10"}, + {I2C_MASTER_CH_11, 0x71, 2, SFP, "SFP11"}, {I2C_MASTER_CH_11, 0x71, 3, SFP, "SFP12"}, + {I2C_MASTER_CH_11, 0x71, 4, SFP, "SFP13"}, {I2C_MASTER_CH_11, 0x71, 5, SFP, "SFP14"}, + {I2C_MASTER_CH_11, 0x71, 6, SFP, "SFP15"}, {I2C_MASTER_CH_11, 0x71, 7, SFP, "SFP16"}, + + {I2C_MASTER_CH_11, 0x72, 0, SFP, "SFP17"}, {I2C_MASTER_CH_11, 0x72, 1, SFP, "SFP18"}, + {I2C_MASTER_CH_11, 0x72, 2, SFP, "SFP19"}, {I2C_MASTER_CH_11, 0x72, 3, SFP, "SFP20"}, + {I2C_MASTER_CH_11, 0x72, 4, SFP, "SFP21"}, {I2C_MASTER_CH_11, 0x72, 5, SFP, "SFP22"}, + {I2C_MASTER_CH_11, 0x72, 6, SFP, "SFP23"}, {I2C_MASTER_CH_11, 0x72, 7, SFP, "SFP24"}, + + {I2C_MASTER_CH_11, 0x73, 0, SFP, "SFP25"}, {I2C_MASTER_CH_11, 0x73, 1, SFP, "SFP26"}, + {I2C_MASTER_CH_11, 0x73, 2, SFP, "SFP27"}, {I2C_MASTER_CH_11, 0x73, 3, SFP, "SFP28"}, + {I2C_MASTER_CH_11, 0x73, 4, SFP, "SFP29"}, {I2C_MASTER_CH_11, 0x73, 5, SFP, "SFP30"}, + {I2C_MASTER_CH_11, 0x73, 6, SFP, "SFP31"}, {I2C_MASTER_CH_11, 0x73, 7, SFP, "SFP32"}, + + {I2C_MASTER_CH_12, 0x70, 0, SFP, "SFP33"}, {I2C_MASTER_CH_12, 0x70, 1, SFP, "SFP34"}, + {I2C_MASTER_CH_12, 0x70, 2, SFP, "SFP35"}, {I2C_MASTER_CH_12, 0x70, 3, SFP, "SFP36"}, + {I2C_MASTER_CH_12, 0x70, 4, SFP, "SFP37"}, {I2C_MASTER_CH_12, 0x70, 5, SFP, "SFP38"}, + {I2C_MASTER_CH_12, 0x70, 6, SFP, "SFP39"}, {I2C_MASTER_CH_12, 0x70, 7, SFP, "SFP40"}, + + /* BUS12 SFP Exported as virtual bus */ + {I2C_MASTER_CH_12, 0x71, 0, SFP, "SFP41"}, {I2C_MASTER_CH_12, 0x71, 1, SFP, "SFP42"}, + {I2C_MASTER_CH_12, 0x71, 2, SFP, "SFP43"}, {I2C_MASTER_CH_12, 0x71, 3, SFP, "SFP44"}, + {I2C_MASTER_CH_12, 0x71, 4, SFP, "SFP45"}, {I2C_MASTER_CH_12, 0x71, 5, SFP, "SFP46"}, + {I2C_MASTER_CH_12, 0x71, 6, SFP, "SFP47"}, {I2C_MASTER_CH_12, 0x71, 7, SFP, "SFP48"}, + + {I2C_MASTER_CH_12, 0x73, 4, QSFP, "QSFP1"}, {I2C_MASTER_CH_12, 0x73, 5, QSFP, "QSFP2"}, + {I2C_MASTER_CH_12, 0x73, 6, QSFP, "QSFP3"}, {I2C_MASTER_CH_12, 0x73, 7, QSFP, "QSFP4"}, + {I2C_MASTER_CH_12, 0x73, 0, QSFP, "QSFP5"}, {I2C_MASTER_CH_12, 0x73, 1, QSFP, "QSFP6"}, + {I2C_MASTER_CH_12, 0x73, 2, QSFP, "QSFP7"}, {I2C_MASTER_CH_12, 0x73, 3, QSFP, "QSFP8"}, + + /* Vritual I2C adapters */ + {I2C_MASTER_CH_1, 0xFF, 0, NONE, "I2C_1"}, // FAN + {I2C_MASTER_CH_2, 0xFF, 0, NONE, "I2C_2"}, + {I2C_MASTER_CH_3, 0xFF, 0, NONE, "I2C_3"}, + {I2C_MASTER_CH_4, 0xFF, 0, NONE, "I2C_4"}, + {I2C_MASTER_CH_5, 0xFF, 0, NONE, "I2C_5"}, // BB + {I2C_MASTER_CH_6, 0xFF, 0, NONE, "I2C_6"}, + {I2C_MASTER_CH_7, 0xFF, 0, NONE, "I2C_7"}, // SW + + // NOTE: Two buses below are for front panel port debug + {I2C_MASTER_CH_11, 0xFF, 0, NONE, "I2C_11"}, // SFF + {I2C_MASTER_CH_12, 0xFF, 0, NONE, "I2C_12"}, + +}; + +#define VIRTUAL_I2C_PORT_LENGTH ARRAY_SIZE(fpga_i2c_bus_dev) +#define FAN_I2C_CPLD_INDEX SFF_PORT_TOTAL +#define BB_I2C_CPLD_INDEX SFF_PORT_TOTAL + 4 +#define SW_I2C_CPLD_INDEX SFF_PORT_TOTAL + 6 +#define NUM_SWITCH_CPLDS 2 + +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 questone2bd_fpga_data - Private data for questone2bd switchboard driver. + * @sff_device: List of optical module device node. + * @i2c_client: List of optical module I2C client device. + * @i2c_adapter: List of I2C adapter populates by this driver. + * @fpga_lock: FPGA internal locks for register access. + * @sw_cpld_locks: Switch CPLD xcvr resource lock. + * @fpga_read_addr: Buffer point to FPGA's register. + * @cpld1_read_addr: Buffer point to CPLD1's register. + * @cpld2_read_addr: Buffer point to CPLD2's register. + * + * For *_read_addr, its temporary hold the register address to be read by + * getreg sysfs, see *getreg() for more details. + */ +struct questone2bd_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; + struct mutex sw_cpld_locks[NUM_SWITCH_CPLDS]; + 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 questone2bd_fpga_data *fpga_data; + +/* + * Kernel object for other module drivers. + * Other module can use these kobject as a parent. + */ + +static struct kobject *fpga = NULL; +static struct kobject *cpld1 = NULL; +static struct kobject *cpld2 = NULL; + +/** + * Device node in sysfs tree. + */ +static struct device *sff_dev = NULL; + +/** + * Show the value of the register set by 'set_fpga_reg_address' + * If the address is not set by 'set_fpga_reg_address' first, + * The version register is selected by default. + * @param buf register value in hextring + * @return number of bytes read, or an error code + */ +static ssize_t get_fpga_reg_value(struct device *dev, struct device_attribute *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); +} +/** + * Store the register address + * @param buf address wanted to be read value of + * @return number of bytes stored, or an error code + */ +static ssize_t set_fpga_reg_address(struct device *dev, struct device_attribute *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; +} +/** + * Show value of fpga scratch register + * @param buf register value in hexstring + * @return number of bytes read, or an error code + */ +static ssize_t get_fpga_scratch(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + return sprintf(buf, "0x%8.8x\n", ioread32(fpga_dev.data_base_addr + FPGA_SCRATCH) & 0xffffffff); +} +/** + * Store value of fpga scratch register + * @param buf scratch register value passing from user space + * @return number of bytes stored, or an error code + */ +static ssize_t set_fpga_scratch(struct device *dev, struct device_attribute *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; +} +/** + * Store a value in a specific register address + * @param buf the value and address in format '0xhhhh 0xhhhhhhhh' + * @return number of bytes sent by user space, or an error code + */ +static ssize_t set_fpga_reg_value(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + // register are 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; +} + +/* 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 struct attribute *fpga_attrs[] = { + &dev_attr_getreg.attr, + &dev_attr_scratch.attr, + &dev_attr_setreg.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[SW_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[SW_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[SW_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[SW_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[SW_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[SW_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[SW_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[SW_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) +{ + u8 data; + int err; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + err = i2c_xcvr_access(I2C_XCVR_STAT,portid,&data,I2C_SMBUS_READ); + if(err < 0){ + return err; + } + return sprintf(buf, "%d\n", (data >> I2C_STAT_INT_N) & 1U); +} +DEVICE_ATTR_RO(qsfp_modirq); + +static ssize_t qsfp_modprs_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u8 data; + int err; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + err = i2c_xcvr_access(I2C_XCVR_STAT,portid,&data,I2C_SMBUS_READ); + if(err < 0){ + return err; + } + return sprintf(buf, "%d\n", (data >> I2C_STAT_MODABS) & 1U); +} +DEVICE_ATTR_RO(qsfp_modprs); + +static ssize_t sfp_txfault_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u8 data; + int err; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + err = i2c_xcvr_access(I2C_XCVR_STAT,portid,&data,I2C_SMBUS_READ); + if(err < 0){ + return err; + } + return sprintf(buf, "%d\n", (data >> I2C_STAT_TXFAULT) & 1U); +} +DEVICE_ATTR_RO(sfp_txfault); + +static ssize_t sfp_rxlos_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u8 data; + int err; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + err = i2c_xcvr_access(I2C_XCVR_STAT,portid,&data,I2C_SMBUS_READ); + if(err < 0){ + return err; + } + return sprintf(buf, "%d\n", (data >> I2C_STAT_RXLOS) & 1U); +} +DEVICE_ATTR_RO(sfp_rxlos); + +static ssize_t sfp_modabs_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u8 data; + int err; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + err = i2c_xcvr_access(I2C_XCVR_STAT,portid,&data,I2C_SMBUS_READ); + if(err < 0){ + return err; + } + return sprintf(buf, "%d\n", (data >> I2C_STAT_MODABS) & 1U); +} +DEVICE_ATTR_RO(sfp_modabs); + +static ssize_t qsfp_modsel_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u8 data; + int err; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + err = i2c_xcvr_access(I2C_XCVR_CTRL,portid,&data,I2C_SMBUS_READ); + if(err < 0){ + return err; + } + return sprintf(buf, "%d\n", (data >> I2C_CTRL_MODSEL) & 1U); +} +static ssize_t qsfp_modsel_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + ssize_t status; + long value; + u8 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + + status = kstrtol(buf, 0, &value); + if (status == 0) { + // if value is 0, clear bit. + i2c_xcvr_access(I2C_XCVR_CTRL,portid,&data,I2C_SMBUS_READ); + if (!value) + data = data & ~( 1U << I2C_CTRL_MODSEL ); + else + data = data | ( 1U << I2C_CTRL_MODSEL ); + i2c_xcvr_access(I2C_XCVR_CTRL,portid,&data,I2C_SMBUS_WRITE); + status = size; + } + return status; +} +DEVICE_ATTR_RW(qsfp_modsel); + +static ssize_t qsfp_reset_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u8 data; + int err; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + err = i2c_xcvr_access(I2C_XCVR_CTRL,portid,&data,I2C_SMBUS_READ); + if(err < 0){ + return err; + } + return sprintf(buf, "%d\n", (data >> I2C_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; + u8 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + + status = kstrtol(buf, 0, &value); + if (status == 0) { + // if value is 0, reset signal is low + i2c_xcvr_access(I2C_XCVR_CTRL,portid,&data,I2C_SMBUS_READ); + if (!value) + data = data & ~((u8)0x1 << I2C_CTRL_RST); + else + data = data | ((u8)0x1 << I2C_CTRL_RST); + i2c_xcvr_access(I2C_XCVR_CTRL,portid,&data,I2C_SMBUS_WRITE); + status = size; + } + return status; +} +DEVICE_ATTR_RW(qsfp_reset); + +static ssize_t sfp_txdisable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u8 data; + int err; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + err = i2c_xcvr_access(I2C_XCVR_CTRL,portid,&data,I2C_SMBUS_READ); + if(err < 0){ + return err; + } + return sprintf(buf, "%d\n", (data >> I2C_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; + u8 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtol(buf, 0, &value); + if (status == 0) { + // check if value is 0 clear + i2c_xcvr_access(I2C_XCVR_CTRL,portid,&data,I2C_SMBUS_READ); + if (!value) + data = data & ~((u8)0x1 << I2C_CTRL_TXDIS); + else + data = data | ((u8)0x1 << I2C_CTRL_TXDIS); + i2c_xcvr_access(I2C_XCVR_CTRL,portid,&data,I2C_SMBUS_WRITE); + 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_modsel.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[SW_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[SW_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[SW_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[SW_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 green/amber/both/alt-blink/OFF + __u8 led_color1, led_color2; + int err; + err = fpga_i2c_access(fpga_data->i2c_adapter[SW_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[SW_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 == 0x07 ? "off" : led_color1 == 0x06 ? "green" : led_color1 == 0x05 ? "amber" : led_color1 == 0x04 ? + "both" : "alt-blink", + led_color1 == 0x07 ? "off" : led_color1 == 0x06 ? "green" : led_color1 == 0x05 ? "amber" : led_color1 == 0x04 ? + "both" : "alt-blink"); +} + +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 = 0x07; + } else if (sysfs_streq(buf, "green")) { + led_color = 0x06; + } else if (sysfs_streq(buf, "amber")) { + led_color = 0x05; + } else if (sysfs_streq(buf, "both")) { + led_color = 0x04; + } else if (sysfs_streq(buf, "alt-blink")) { + led_color = 0x03; + } else { + status = -EINVAL; + return status; + } + status = fpga_i2c_access(fpga_data->i2c_adapter[SW_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[SW_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 * questone2bd_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_core_init(unsigned int master_bus, unsigned int freq_div,void __iomem *pci_bar){ + + unsigned int ctrl; + unsigned int REG_FREQ_L; + unsigned int REG_FREQ_H; + unsigned int REG_CTRL; + unsigned int REG_CMD; + + REG_FREQ_L = I2C_MASTER_FREQ_L + (master_bus - 1) * 0x20; + REG_FREQ_H = I2C_MASTER_FREQ_H + (master_bus - 1) * 0x20; + REG_CTRL = I2C_MASTER_CTRL + (master_bus - 1) * 0x20; + REG_CMD = I2C_MASTER_CMD + (master_bus - 1) * 0x20; + + if ( freq_div != I2C_DIV_100K ) { + printk(KERN_ERR "FPGA I2C core: Unsupported clock divider: %x\n", freq_div); + return -EINVAL; + } + + // Makes sure core is disable + ctrl = ioread8(pci_bar + REG_CTRL); + iowrite8( ctrl & ~(1 << I2C_CTRL_EN), pci_bar + REG_CTRL); + iowrite8( freq_div & 0xFF , pci_bar + REG_FREQ_L); + iowrite8( freq_div >> 8, pci_bar + REG_FREQ_H); + iowrite8(1 << I2C_CTRL_EN, pci_bar + REG_CTRL); + + return 0; +} + +static void i2c_core_deinit(unsigned int master_bus,void __iomem *pci_bar){ + + unsigned int REG_CTRL; + REG_CTRL = I2C_MASTER_CTRL + (master_bus - 1) * 0x20; + // Disable core + iowrite8( ioread8(pci_bar + REG_CTRL) & ~(1 << I2C_CTRL_EN), pci_bar + REG_CTRL); +} + +/* + * i2c_xcvr_access() - Optical port xcvr accessor through Switch CPLDs. + * @register_address: The xcvr register address. + * @portid: Optical module port index, start from 1. + * @data: Buffer to get or set data. + * @rw: I2C_SMBUS_READ or I2C_SMBUS_WRITE flag. + * + * questone2bd have 2 switch CPLDs. Each CPLD manages 28 ports. + * + * +------------------+------------------+ + * |1 CPLD1 27|29 CPLD2 55| + * |2 28|30 56| + * +------------------+------------------+ + * + * Return: 0 if success, error code less than zero if fails. + */ +static int i2c_xcvr_access(u8 register_address, unsigned int portid, u8 *data, char rw){ + + u16 dev_addr = 0; + int err; + unsigned int sw_cpld_lock_index = 0; + + /* check for portid valid length */ + if(portid < 0 || portid > SFF_PORT_TOTAL){ + return -EINVAL; + } + if (portid <= 28 ){ + dev_addr = CPLD1_SLAVE_ADDR; + sw_cpld_lock_index = 0; + }else{ + dev_addr = CPLD2_SLAVE_ADDR; + portid = portid - 28; + sw_cpld_lock_index = 1; + } + + mutex_lock(&fpga_data->sw_cpld_locks[sw_cpld_lock_index]); + + // Select port + err = fpga_i2c_access(fpga_data->i2c_adapter[SW_I2C_CPLD_INDEX], + dev_addr, + 0x00, + I2C_SMBUS_WRITE, + I2C_XCVR_SEL, + I2C_SMBUS_BYTE_DATA, + (union i2c_smbus_data*)&portid); + if(err < 0){ + goto exit_unlock; + } + // Read/write port xcvr register + err = fpga_i2c_access(fpga_data->i2c_adapter[SW_I2C_CPLD_INDEX], + dev_addr, + 0x00, + rw, + register_address, + I2C_SMBUS_BYTE_DATA, + (union i2c_smbus_data*)data); + if(err < 0){ + goto exit_unlock; + } + +exit_unlock: + mutex_unlock(&fpga_data->sw_cpld_locks[sw_cpld_lock_index]); + return err; +} + +static int i2c_wait_stop(struct i2c_adapter *a, unsigned long timeout, int writing) { + int error = 0; + int Status; + unsigned int master_bus; + + struct i2c_dev_data *new_data; + void __iomem *pci_bar = fpga_dev.data_base_addr; + + unsigned int REG_FREQ_L; + unsigned int REG_FREQ_H; + unsigned int REG_CMD; + unsigned int REG_CTRL; + unsigned int REG_STAT; + unsigned int REG_DATA; + + /* Sanity check for the NULL pointer */ + if (a == NULL) + return -ESHUTDOWN; + else + new_data = i2c_get_adapdata(a); + + if (new_data == NULL) + return -ESHUTDOWN; + + 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_FREQ_L = I2C_MASTER_FREQ_L + (master_bus - 1) * 0x20; + REG_FREQ_H = I2C_MASTER_FREQ_H + (master_bus - 1) * 0x20; + REG_CTRL = I2C_MASTER_CTRL + (master_bus - 1) * 0x20; + REG_CMD = I2C_MASTER_CMD + (master_bus - 1) * 0x20; + REG_STAT = I2C_MASTER_STATUS + (master_bus - 1) * 0x20; + REG_DATA = I2C_MASTER_DATA + (master_bus - 1) * 0x20; + + check(pci_bar + REG_STAT); + check(pci_bar + REG_CTRL); + + dev_dbg(&a->dev,"ST:%2.2X\n", ioread8(pci_bar + REG_STAT)); + timeout = jiffies + msecs_to_jiffies(timeout); + while (1) { + Status = ioread8(pci_bar + REG_STAT); + dev_dbg(&a->dev,"ST:%2.2X\n", Status); + if (time_after(jiffies, timeout)) { + info("Status %2.2X", Status); + info("Error Timeout"); + error = -ETIMEDOUT; + break; + } + + if ( (Status & ( 1 << I2C_STAT_BUSY )) == 0 ) + { + dev_dbg(&a->dev," IF:%2.2X\n", Status); + break; + } + + cpu_relax(); + cond_resched(); + } + info("Status %2.2X", Status); + info("STA:%x",Status); + + if (error < 0) { + info("Status %2.2X", Status); + return error; + } + return 0; +} + +static int i2c_wait_ack(struct i2c_adapter *a, unsigned long timeout, int writing) { + int error = 0; + int Status; + unsigned int master_bus; + + struct i2c_dev_data *new_data; + void __iomem *pci_bar = fpga_dev.data_base_addr; + + unsigned int REG_FREQ_L; + unsigned int REG_FREQ_H; + unsigned int REG_CMD; + unsigned int REG_CTRL; + unsigned int REG_STAT; + unsigned int REG_DATA; + + /* Sanity check for the NULL pointer */ + if (a == NULL) + return -ESHUTDOWN; + else + new_data = i2c_get_adapdata(a); + + if (new_data == NULL) + return -ESHUTDOWN; + + 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_FREQ_L = I2C_MASTER_FREQ_L + (master_bus - 1) * 0x20; + REG_FREQ_H = I2C_MASTER_FREQ_H + (master_bus - 1) * 0x20; + REG_CTRL = I2C_MASTER_CTRL + (master_bus - 1) * 0x20; + REG_CMD = I2C_MASTER_CMD + (master_bus - 1) * 0x20; + REG_STAT = I2C_MASTER_STATUS + (master_bus - 1) * 0x20; + REG_DATA = I2C_MASTER_DATA + (master_bus - 1) * 0x20; + + check(pci_bar + REG_STAT); + check(pci_bar + REG_CTRL); + + dev_dbg(&a->dev,"ST:%2.2X\n", ioread8(pci_bar + REG_STAT)); + timeout = jiffies + msecs_to_jiffies(timeout); + while (1) { + Status = ioread8(pci_bar + REG_STAT); + dev_dbg(&a->dev,"ST:%2.2X\n", Status); + + if ( (Status & ( 1 << I2C_STAT_TIP )) == 0 ) + { + dev_dbg(&a->dev," IF:%2.2X\n", Status); + break; + } + + if (time_after(jiffies, timeout)) { + info("Status %2.2X", Status); + info("Error Timeout"); + error = -ETIMEDOUT; + break; + } + + cpu_relax(); + cond_resched(); + } + info("Status %2.2X", Status); + info("STA:%x",Status); + + if (error < 0) { + info("Status %2.2X", Status); + + return error; + } + + /** There is only one master in each bus. If this error happen something is + * not normal in i2c transfer refer to: + * https://www.i2c-bus.org/i2c-primer/analysing-obscure-problems/master-reports-arbitration-lost + */ + // Arbitration lost + if (Status & (1 << I2C_STAT_AL)) { + info("Error arbitration lost"); + nack_retry[master_bus - 1] = 1; + return -EBUSY; + } + + // Ack not received + if (Status & (1 << I2C_STAT_RxACK)) { + info( "SL No ACK"); + if (writing) { + info("Error No ACK"); + nack_retry[master_bus - 1] = 1; + return -EIO; + } + } else { + info( "SL ACK"); + } + + return error; +} + +/* SMBUS Xfer for opencore I2C with polling */ +// TODO: Change smbus_xfer to master_xfer - This will support i2c and all smbus emu functions. +static int smbus_access(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data) +{ + int error = 0; + int cnt = 0; + int bid = 0; + struct i2c_dev_data *dev_data; + void __iomem *pci_bar; + unsigned int portid, master_bus; + + unsigned int REG_FREQ_L; + unsigned int REG_FREQ_H; + unsigned int REG_CMD; + unsigned int REG_CTRL; + unsigned int REG_STAT; + unsigned int REG_DATA; + + REG_FREQ_L = 0; + REG_FREQ_H = 0; + REG_CTRL = 0; + REG_CMD = 0; + REG_STAT = 0; + REG_DATA = 0; + + /* Sanity check for the NULL pointer */ + if (adapter == NULL) + return -ESHUTDOWN; + else + dev_data = i2c_get_adapdata(adapter); + + if (dev_data == NULL) + return -ESHUTDOWN; + + portid = dev_data->portid; + pci_bar = fpga_dev.data_base_addr; + master_bus = dev_data->pca9548.master_bus; + +#ifdef DEBUG_KERN + printk(KERN_INFO "portid %2d|@ 0x%2.2X|f 0x%4.4X|(%d)%-5s| (%d)%-10s|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); + return -EOPNOTSUPP; + } + + if (master_bus < I2C_MASTER_CH_1 || master_bus > I2C_MASTER_CH_TOTAL) { + return -EINVAL; + } + + error = i2c_core_init(master_bus, I2C_DIV_100K, fpga_dev.data_base_addr); + + REG_FREQ_L = I2C_MASTER_FREQ_L + (master_bus - 1) * 0x20; + REG_FREQ_H = I2C_MASTER_FREQ_H + (master_bus - 1) * 0x20; + REG_CTRL = I2C_MASTER_CTRL + (master_bus - 1) * 0x20; + REG_CMD = I2C_MASTER_CMD + (master_bus - 1) * 0x20; + REG_STAT = I2C_MASTER_STATUS + (master_bus - 1) * 0x20; + REG_DATA = I2C_MASTER_DATA + (master_bus - 1) * 0x20; + + ////[S][ADDR/R] + 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_DATA); + } else { + // sent device address with Write mode + iowrite8( (addr << 1) & 0xFE, pci_bar + REG_DATA); + } + iowrite8( 1 << I2C_CMD_STA | 1 << I2C_CMD_WR, pci_bar + REG_CMD); + + info( "MS Start"); + + //// Wait {A} + // + IACK + error = i2c_wait_ack(adapter, 30, 1); + if (error < 0) { + info( "get error %d", error); + dev_dbg(&adapter->dev,"START Error: %d\n", 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_DATA); + // Start the transfer + iowrite8(1 << I2C_CMD_WR, pci_bar + REG_CMD); + info( "MS Send CMD 0x%2.2X", cmd); + + // Wait {A} + // IACK + error = i2c_wait_ack(adapter, 30, 1); + if (error < 0) { + info( "get error %d", error); + dev_dbg(&adapter->dev,"CMD Error: %d\n", 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 mode 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_DATA); + //Start the transfer + iowrite8(1 << I2C_CMD_WR, pci_bar + REG_CMD); + info( "MS Send CNT 0x%2.2X", cnt); + + // Wait {A} + // IACK + error = i2c_wait_ack(adapter, 30, 1); + if (error < 0) { + info( "get error %d", error); + dev_dbg(&adapter->dev,"CNT Error: %d\n", error); + goto Done; + } + } + + // [DATA]{A} + if ( rw == I2C_SMBUS_WRITE && ( + size == I2C_SMBUS_BYTE || + size == I2C_SMBUS_BYTE_DATA || + size == I2C_SMBUS_WORD_DATA || + size == I2C_SMBUS_BLOCK_DATA || + size == I2C_SMBUS_I2C_BLOCK_DATA + )) { + int bid = 0; + info( "MS prepare to sent [%d bytes]", cnt); + if (size == I2C_SMBUS_BLOCK_DATA || size == I2C_SMBUS_I2C_BLOCK_DATA) { + bid = 1; // block[0] is cnt; + cnt += 1; // offset from block[0] + } + for (; bid < cnt; bid++) { + info("STA:%x", ioread8(pci_bar + REG_STAT) ); + info( " Data > %2.2X", data->block[bid]); + iowrite8(data->block[bid], pci_bar + REG_DATA); + iowrite8(1 << I2C_CMD_WR, pci_bar + REG_CMD); + + // Wait {A} + // IACK + error = i2c_wait_ack(adapter, 30, 1); + if (error < 0) { + dev_dbg(&adapter->dev,"Send DATA Error: %d\n", error); + 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"); + + // sent Address with Read mode + iowrite8( addr << 1 | 0x1 , pci_bar + REG_DATA); + // SET START | WRITE + iowrite8( 1 << I2C_CMD_STA | 1 << I2C_CMD_WR, pci_bar + REG_CMD); + + // Wait {A} + error = i2c_wait_ack(adapter, 30, 1); + if (error < 0) { + dev_dbg(&adapter->dev,"Repeat START Error: %d\n", error); + 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; + } + + info( "MS Receive"); + + for (bid = 0; bid < cnt; bid++) { + + // Start receive FSM + if (bid == cnt - 1) { + info( "READ NACK"); + iowrite8(1 << I2C_CMD_RD | 1 << I2C_CMD_ACK, pci_bar + REG_CMD); + }else{ + + iowrite8(1 << I2C_CMD_RD, pci_bar + REG_CMD); + } + + // Wait {A} + error = i2c_wait_ack(adapter, 30, 0); + if(nack_retry[master_bus - 1] == 1) + { + need_retry[master_bus - 1] = 1; + } + if (error < 0) { + dev_dbg(&adapter->dev,"Receive DATA Error: %d\n", error); + goto Done; + } + if(size == I2C_SMBUS_I2C_BLOCK_DATA){ + /* block[0] is read length */ + data->block[bid+1] = ioread8(pci_bar + REG_DATA); + info( "DATA IN [%d] %2.2X", bid+1, data->block[bid+1]); + }else { + data->block[bid] = ioread8(pci_bar + REG_DATA); + info( "DATA IN [%d] %2.2X", bid, data->block[bid]); + } + if (size == I2C_SMBUS_BLOCK_DATA && bid == 0) { + cnt = data->block[0] + 1; + } + } + } + +Done: + info( "MS STOP"); + // SET STOP + iowrite8( 1 << I2C_CMD_STO, pci_bar + REG_CMD); + // Wait for the STO to finish. + i2c_wait_stop(adapter, 30, 0); + check(pci_bar + REG_CTRL); + check(pci_bar + REG_STAT); +#ifdef DEBUG_KERN + printk(KERN_INFO "END --- Error code %d", error); +#endif + + return error; +} + +/** + * Wrapper of smbus_access access with PCA9548 I2C switch management. + * This function set PCA9548 switches to the proper slave channel. + * Only one channel among switches chip is selected during communication time. + * + * Note: If the bus does not have any PCA9548 on it, the switch_addr must be + * set to 0xFF, it will use normal smbus_access function. + */ +static int fpga_i2c_access(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data) +{ + int error, retval = 0; + struct i2c_dev_data *dev_data; + unsigned char master_bus; + unsigned char switch_addr; + unsigned char channel; + unsigned char *calling_name; + uint16_t prev_port = 0; + unsigned char prev_switch; + unsigned char prev_ch; + uint8_t read_channel; + int retry = 0; + + /* Sanity check for the NULL pointer */ + if (adapter == NULL) + return -ESHUTDOWN; + else + dev_data = i2c_get_adapdata(adapter); + + if (dev_data == NULL) + return -ESHUTDOWN; + + master_bus = dev_data->pca9548.master_bus; + switch_addr = dev_data->pca9548.switch_addr; + channel = dev_data->pca9548.channel; + calling_name = dev_data->pca9548.calling_name; + + // 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 + // Only select new channel of a switch if they are difference from last channel of a switch + 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 select ch %d of 0x%x, CODE %d\n", channel, switch_addr, 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 select ch %d of 0x%x, CODE %d\n", channel, switch_addr, 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 + nack_retry[master_bus - 1] = 0; + need_retry[master_bus - 1] = 0; + error = smbus_access(adapter, addr, flags, rw, cmd, size, data); + if((nack_retry[master_bus - 1]==1)&&(need_retry[master_bus - 1]==1)) + retry = 2000; + else + retry = 5; + // If the first access failed, do retry. + while((nack_retry[master_bus - 1]==1)&&retry) + { + retry--; + nack_retry[master_bus - 1] = 0; + dev_dbg(&adapter->dev,"error = %d\n",error); + error = smbus_access(adapter, addr, flags, rw, cmd, size, data); + dev_dbg(&adapter->dev,"nack retry = %d\n",retry); + } + nack_retry[master_bus - 1] = 0; + need_retry[master_bus - 1] = 0; + + retval = error; + + 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); + }else{ + goto release_unlock; + } + + /** For the bus with PCA9548, try to read PCA9548 one more time. + * For the bus w/o PCA9548 just check the return from last time. + */ + if (switch_addr != 0xFF) { + error = smbus_access(adapter, switch_addr, flags, I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE, (union i2c_smbus_data*)&read_channel); + dev_dbg(&adapter->dev,"Try access I2C switch device at %2.2x\n", switch_addr); + if(error < 0){ + dev_dbg(&adapter->dev,"Unbale to access switch device.\n"); + }else{ + dev_dbg(&adapter->dev,"Read success, register val %2.2x\n", read_channel); + } + } + + // If retry was used up(retry = 0) and the last transfer result is -EBUSY + if(retry <= 0 && error == -EBUSY ){ + retval = error; + // raise device error message + dev_err(&adapter->dev, "I2C bus hangup detected on %s port.\n", calling_name); + + /** + * questone2bd: Device specific I2C reset topology + */ + if( master_bus == I2C_MASTER_CH_11 || master_bus == I2C_MASTER_CH_12 ){ + dev_notice(&adapter->dev, "Trying bus recovery...\n"); + dev_notice(&adapter->dev, "Reset I2C switch device.\n"); + + // reset PCA9548 on the current BUS. + if( master_bus == I2C_MASTER_CH_11){ + iowrite8( ioread8(fpga_dev.data_base_addr + 0x0108) & 0xF0, fpga_dev.data_base_addr + 0x0108); + udelay(1); + iowrite8( ioread8(fpga_dev.data_base_addr + 0x0108) | 0x0F, fpga_dev.data_base_addr + 0x0108); + }else if(master_bus == I2C_MASTER_CH_12){ + iowrite8( ioread8(fpga_dev.data_base_addr + 0x0108) & 0x8F, fpga_dev.data_base_addr + 0x0108); + udelay(1); + iowrite8( ioread8(fpga_dev.data_base_addr + 0x0108) | 0x70, fpga_dev.data_base_addr + 0x0108); + } + // clear the last access port + fpga_i2c_lasted_access_port[master_bus - 1] = 0; + }else{ + dev_crit(&adapter->dev, "I2C bus unrecoverable.\n"); + } + } + + +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 retval; +} + +/** + * 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 questone2bd_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 * questone2bd_i2c_init(struct platform_device *pdev, int portid, int bus_number_offset) +{ + int error; + + struct i2c_adapter *new_adapter; + struct i2c_dev_data *new_data; + + new_adapter = kzalloc(sizeof(*new_adapter), GFP_KERNEL); + if (!new_adapter) { + printk(KERN_ALERT "Cannot alloc i2c adapter for %s", fpga_i2c_bus_dev[portid].calling_name); + return NULL; + } + + new_adapter->owner = THIS_MODULE; + new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + new_adapter->algo = &questone2bd_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_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 questone2bd_resources[] = { + { + .start = 0x10000000, + .end = 0x10001000, + .flags = IORESOURCE_MEM, + }, +}; + +static void questone2bd_dev_release( struct device * dev) +{ + return; +} + +static struct platform_device questone2bd_dev = { + .name = DRIVER_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(questone2bd_resources), + .resource = questone2bd_resources, + .dev = { + .release = questone2bd_dev_release, + } +}; + +/** + * Board info for QSFP/SFP+ eeprom. + * Note: Using OOM optoe as transceiver eeprom driver. + * https://www.opencompute.org/wiki/Networking/SpecsAndDesigns#Open_Optical_Monitoring + */ +static struct i2c_board_info sff8436_eeprom_info[] = { + { I2C_BOARD_INFO("optoe1", 0x50) }, //For QSFP w/ sff8436 + { I2C_BOARD_INFO("optoe2", 0x50) }, //For SFP+ w/ sff8472 +}; + +static int questone2bd_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 questone2bd_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 = 0; ret < NUM_SWITCH_CPLDS; ret++) { + mutex_init(&fpga_data->sw_cpld_locks[ret]); + } + + for (ret = I2C_MASTER_CH_1 ; ret <= I2C_MASTER_CH_TOTAL; ret++) { + mutex_init(&fpga_i2c_master_locks[ret - 1]); + fpga_i2c_lasted_access_port[ret - 1] = 0; + } + + 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 = I2C_MASTER_CH_1; portid_count <= I2C_MASTER_CH_TOTAL; portid_count++){ + if(!allow_unsafe_i2c_access){ + if( portid_count < I2C_MASTER_CH_7 || + portid_count == I2C_MASTER_CH_9 || portid_count == I2C_MASTER_CH_10 ) + continue; + } + ret = i2c_core_init(portid_count, I2C_DIV_100K, fpga_dev.data_base_addr); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to init I2C core %d\n", portid_count); + 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++) { + if(!allow_unsafe_i2c_access){ + if( portid_count >= FAN_I2C_CPLD_INDEX && portid_count < SW_I2C_CPLD_INDEX ){ + fpga_data->i2c_adapter[portid_count] = NULL; + continue; + } + } + fpga_data->i2c_adapter[portid_count] = questone2bd_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] = questone2bd_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[SW_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[SW_I2C_CPLD_INDEX], CPLD2_SLAVE_ADDR, 0x00, + I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&cpld2_version); + + printk(KERN_INFO "Switch CPLD1 Version: %2.2x\n", cpld1_version); + printk(KERN_INFO "Switch CPLD2 Version: %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++) { + + if(!allow_unsafe_i2c_access){ + if( portid_count >= FAN_I2C_CPLD_INDEX && portid_count < SW_I2C_CPLD_INDEX ){ + continue; + } + } + + 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 questone2bd_drv_remove(struct platform_device *pdev) +{ + int portid_count; + struct sff_device_data *rem_data; + struct i2c_dev_data *adap_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]); + adap_data = i2c_get_adapdata(fpga_data->i2c_adapter[portid_count]); + i2c_del_adapter(fpga_data->i2c_adapter[portid_count]); + } + } + + for (portid_count = I2C_MASTER_CH_1; portid_count <= I2C_MASTER_CH_TOTAL; portid_count++){ + if(!allow_unsafe_i2c_access){ + if( portid_count < I2C_MASTER_CH_7 || + portid_count == I2C_MASTER_CH_9 || portid_count == I2C_MASTER_CH_10 ) + continue; + } + i2c_core_deinit(portid_count, fpga_dev.data_base_addr); + } + + 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 questone2bd_drv = { + .probe = questone2bd_drv_probe, + .remove = __exit_p(questone2bd_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 = ioremap_nocache(fpga_dev.data_mmio_start, fpga_dev.data_mmio_len); + if (!fpga_dev.data_base_addr) { + dev_err(dev, "cannot iomap region of size %lu\n", + (unsigned long)fpga_dev.data_mmio_len); + goto pci_release; + } + dev_info(dev, "data_mmio iomap base = 0x%lx \n", + (unsigned long)fpga_dev.data_base_addr); + dev_info(dev, "data_mmio_start = 0x%lx data_mmio_len = %lu\n", + (unsigned long)fpga_dev.data_mmio_start, + (unsigned long)fpga_dev.data_mmio_len); + + printk(KERN_INFO "FPGA PCIe driver probe OK.\n"); + printk(KERN_INFO "FPGA ioremap registers of size %lu\n", (unsigned long)fpga_dev.data_mmio_len); + printk(KERN_INFO "FPGA Virtual BAR %d at %8.8lx - %8.8lx\n", FPGA_PCI_BAR_NUM, + (unsigned long)fpga_dev.data_base_addr, + (unsigned long)(fpga_dev.data_base_addr + fpga_dev.data_mmio_len)); + printk(KERN_INFO ""); + fpga_version = ioread32(fpga_dev.data_base_addr); + printk(KERN_INFO "FPGA Version : %8.8x\n", fpga_version); + if ((err = fpgafw_init()) < 0){ + goto pci_release; + } + platform_device_register(&questone2bd_dev); + platform_driver_register(&questone2bd_drv); + return 0; + +pci_release: + pci_release_regions(pdev); +pci_disable: + pci_disable_device(pdev); + return err; +} + +static void fpga_pci_remove(struct pci_dev *pdev) +{ + platform_driver_unregister(&questone2bd_drv); + platform_device_unregister(&questone2bd_dev); + fpgafw_exit(); + pci_iounmap(pdev, fpga_dev.data_base_addr); + pci_release_regions(pdev); + pci_disable_device(pdev); + printk(KERN_INFO "FPGA PCIe driver remove OK.\n"); +}; + +static struct pci_driver pci_dev_ops = { + .name = FPGA_PCI_NAME, + .probe = fpga_pci_probe, + .remove = fpga_pci_remove, + .id_table = fpga_id_table, +}; + +enum { + READREG, + WRITEREG +}; + +struct fpga_reg_data { + uint32_t addr; + uint32_t value; +}; + +static long fpgafw_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + int ret = 0; + struct fpga_reg_data data; + mutex_lock(&fpga_data->fpga_lock); + +#ifdef TEST_MODE + static uint32_t status_reg; +#endif + // Switch function to read and write. + switch (cmd) { + case READREG: + if (copy_from_user(&data, (void __user*)arg, sizeof(data)) != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EFAULT; + } + data.value = ioread32(fpga_dev.data_base_addr + data.addr); + if (copy_to_user((void __user*)arg , &data, sizeof(data)) != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EFAULT; + } +#ifdef TEST_MODE + if (data.addr == 0x1210) { + switch (status_reg) { + case 0x0000 : status_reg = 0x8000; + break; + + case 0x8080 : status_reg = 0x80C0; + break; + case 0x80C0 : status_reg = 0x80F0; + break; + case 0x80F0 : status_reg = 0x80F8; + break; + + } + iowrite32(status_reg, fpga_dev.data_base_addr + 0x1210); + } +#endif + + + break; + case WRITEREG: + if (copy_from_user(&data, (void __user*)arg, sizeof(data)) != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EFAULT; + } + iowrite32(data.value, fpga_dev.data_base_addr + data.addr); + +#ifdef TEST_MODE + if (data.addr == 0x1204) { + status_reg = 0x8080; + iowrite32(status_reg, fpga_dev.data_base_addr + 0x1210); + } +#endif + + break; + default: + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + mutex_unlock(&fpga_data->fpga_lock); + return ret; +} + + +const struct file_operations fpgafw_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = fpgafw_unlocked_ioctl, +}; + + +static int fpgafw_init(void) { + printk(KERN_INFO "Initializing the switchboard driver\n"); + // Try to dynamically allocate a major number for the device -- more difficult but worth it + majorNumber = register_chrdev(0, DEVICE_NAME, &fpgafw_fops); + if (majorNumber < 0) { + printk(KERN_ALERT "Failed to register a major number\n"); + return majorNumber; + } + printk(KERN_INFO "Device registered correctly with major number %d\n", majorNumber); + + // Register the device class + fpgafwclass = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(fpgafwclass)) { // Check for error and clean up if there is + unregister_chrdev(majorNumber, DEVICE_NAME); + printk(KERN_ALERT "Failed to register device class\n"); + return PTR_ERR(fpgafwclass); + } + printk(KERN_INFO "Device class registered correctly\n"); + + // Register the device driver + fpgafwdev = device_create(fpgafwclass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME); + if (IS_ERR(fpgafwdev)) { // Clean up if there is an error + class_destroy(fpgafwclass); // Repeated code but the alternative is goto statements + unregister_chrdev(majorNumber, DEVICE_NAME); + printk(KERN_ALERT "Failed to create the FW upgrade device node\n"); + return PTR_ERR(fpgafwdev); + } + printk(KERN_INFO "FPGA fw upgrade device node created correctly\n"); + return 0; +} + +static void fpgafw_exit(void) { + device_destroy(fpgafwclass, MKDEV(majorNumber, 0)); // remove the device + class_destroy(fpgafwclass); // remove the device class + unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number + printk(KERN_INFO "Goodbye!\n"); +} + +int questone2bd_init(void) +{ + int rc; + rc = pci_register_driver(&pci_dev_ops); + if (rc) + return rc; + return 0; +} + +void questone2bd_exit(void) +{ + pci_unregister_driver(&pci_dev_ops); +} + +module_init(questone2bd_init); +module_exit(questone2bd_exit); + +module_param(allow_unsafe_i2c_access, bool, 0400); +MODULE_PARM_DESC(allow_unsafe_i2c_access, "enable i2c busses despite potential races against BMC bus access"); + +MODULE_AUTHOR("Pradchaya P. "); +MODULE_DESCRIPTION("Celestica questone2bd switchboard platform driver"); +MODULE_VERSION(MOD_VERSION); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/syscpld.c b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/syscpld.c new file mode 100644 index 00000000000..590a3fd48ce --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/modules/syscpld.c @@ -0,0 +1,769 @@ +/* + * SYS CPLD driver for CPLD and compatibles + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "i2c_dev_sysfs.h" + +// #define DEBUG +#define SYSCPLD_ALARM_NODE 0xff /*just for flag using*/ +#define SYSFS_READ 0 +#define SYSFS_WRITE 1 + +#ifdef DEBUG +#define SYSCPLD_DEBUG(fmt, ...) do { \ + printk(KERN_DEBUG "%s:%d " fmt "\n", \ + __FUNCTION__, __LINE__, ##__VA_ARGS__); \ +} while (0) + +#else /* !DEBUG */ + +#define SYSCPLD_DEBUG(fmt, ...) +#endif + + +enum chips { + SYSCPLD = 1, +}; + +struct temp_data_t { + int input; + int max; + int max_hyst; +}; + +struct temp_data { + struct temp_data_t temp1; +}; + +struct temp_data switch_temp_data; +struct temp_data cpu_temp_data; +struct i2c_client *syscpld_client; + +static const struct i2c_device_id syscpld_id[] = { + {"syscpld", SYSCPLD }, + { } +}; + +static int board_type; + +static int temp_value_rw(const char *name, int opcode, int value) +{ + int *p = NULL; + + if (strcmp(name, "temp1_max") == 0) { + p = &switch_temp_data.temp1.max; + } else if (strcmp(name, "temp1_max_hyst") == 0) { + p = &switch_temp_data.temp1.max_hyst; + } else if (strcmp(name, "temp2_input") == 0) { + p = &cpu_temp_data.temp1.input; + } else if (strcmp(name, "temp2_max") == 0) { + p = &cpu_temp_data.temp1.max; + } else if (strcmp(name, "temp2_max_hyst") == 0) { + p = &cpu_temp_data.temp1.max_hyst; + } else { + return -1; + } + + if (opcode == SYSFS_READ) + return *p; + else if (opcode == SYSFS_WRITE) + *p = value; + else + return -1; + + return 0; +} + +static ssize_t switch_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int temp_val = 0; + int freq = i2c_dev_read_word_bigendian(dev, attr); + + if (freq <= 0) + { + freq = 1; + SYSCPLD_DEBUG("Read Swich chip temperature error!\n"); + } + + temp_val = 434100 - (12500000 / freq - 1) * 535; + if (temp_val > 200000 || temp_val < 0) temp_val = 0; + + return scnprintf(buf, PAGE_SIZE, "%d\n", temp_val); +} + +static ssize_t sys_alarm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value = -1; + i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr); + const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr; + const char *name = dev_attr->ida_name; + + if (!name) + return -1; + + value = temp_value_rw(name, SYSFS_READ, 0); + + return scnprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t sys_alarm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + int write_value = 0; + i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr); + const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr; + const char *name = dev_attr->ida_name; + + if (!name) + return -1; + + if (buf == NULL) { + return -ENXIO; + } + + rc = kstrtoint(buf, 10, &write_value); + if (rc != 0) { + return count; + } + rc = temp_value_rw(name, SYSFS_WRITE, write_value); + if (rc < 0) + return -1; + + return count; +} + +static i2c_dev_data_st syscpld_data; +static const i2c_dev_attr_st syscpld_attr_table_questone2bd[] = { + { + "version", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x0, 0, 8, + }, + { + "scratch", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x1, 0, 8, + }, + { + "hardware_version", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x2, 0, 2, + }, + { + "sw_brd_type", + "Indicate the board type\n" + "0x00: fishbone32\n" + "0x01: questone2bd", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x3, 0, 2, + }, + { + "sb_reset", + "switch board reset control:\n" + "0x0: reset\n" + "0x1 not reset", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x4, 0, 1, + }, + { + "i210_reset", + "I210 reset control:\n" + "0x0: reset\n" + "0x1 not reset", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x4, 1, 1, + }, + { + "pca9548_reset", + "PCA9548 reset control:\n" + "0x0: reset\n" + "0x1 not reset", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x4, 2, 1, + }, + { + "fan_cpld_reset", + "FAN CPLD reset control:\n" + "0x0: reset\n" + "0x1 not reset", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x4, 3, 1, + }, + { + "bmc_reset", + "BMC reset control:\n" + "0x0: reset\n" + "0x1 not reset", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x4, 4, 1, + }, + { + "bcm5387_reset", + "BCM5387 reset control:\n" + "0x0: reset\n" + "0x1 not reset", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x4, 5, 1, + }, + { + "tpm_reset", + "TPM reset control:\n" + "0x0: reset\n" + "0x1 not reset", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x4, 6, 1, + }, + { + "come_rst_st", + "0x11: power on reset\n" + "0x22: software trigger CPU to warm reset\n" + "0x33: software trigger CPU to cold reset\n" + "0x44: CPU warm reset\n" + "0x55: CPU cold reset\n" + "0x66: watchdog reset\n" + "0x77: power cycle", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x6, 0, 8, + }, + { + "usb_iso_en", + "0x0: enable\n" + "0x1: disable", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0xa, 0, 1, + }, + { + "usb_front_oc", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0xb, 0, 1, + }, + { + "tps2051_oc", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0xb, 1, 1, + }, + { + "sol_control", + "0x0: switch to BMC\n" + "0x1: switch to COMe", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0xc, 0, 1, + }, + { + "cpu_int_status", + "0x0: interrupt\n" + "0x1: no interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x10, 0, 1, + }, + { + "bmc_int_status", + "0x0: interrupt\n" + "0x1: no interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x10, 1, 1, + }, + { + "sw_lm75_int_status", + "0x0: interrupt\n" + "0x1: no interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x11, 0, 1, + }, + { + "i210_wake_int_status", + "0x0: interrupt\n" + "0x1: no interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x11, 1, 1, + }, + { + "psu1_int", + "0x0: interrupt\n" + "0x1: no interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x11, 2, 1, + }, + { + "psu2_int", + "0x0: interrupt\n" + "0x1: no interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x11, 3, 1, + }, + { + "bmc_54616s_int_status", + "0x0: interrupt\n" + "0x1: no interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x11, 4, 1, + }, + { + "gbe_54616s_int_status", + "0x0: interrupt\n" + "0x1: no interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x11, 5, 1, + }, + { + "gbe_54616s_b_int_status", + "0x0: interrupt\n" + "0x1: no interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x11, 6, 1, + }, + { + "pwr_come_en", + "0x0: COMe power is off\n" + "0x1: COMe power is on", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x20, 0, 1, + }, + { + "come_rst_n", + "0x0: trigger COMe reset\n" + "0x1: normal", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x21, 0, 1, + }, + { + "come_status", + "0x1: SUS_S3_N\n" + "0x2: SUS_S4_N\n" + "0x4: SUS_S5_N\n" + "0x8: SUS_STAT_N", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x22, 0, 4, + }, + { + "bios_cs", + "0x1: select BIOS0\n" + "0x3: select BIOS1", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x23, 0, 2, + }, + { + "bios_ctrl", + "0x0: connect to CPU, control by software\n" + "0x1: disable connect to CPU, control by CPLD\n" + "0x2: connect to BMC, control by software", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x23, 4, 2, + }, + { + "cb_pwr_btn_n", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x24, 0, 1, + }, + { + "cb_type_n", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x25, 0, 3, + }, + { + "switch_power_f", + "0x1: force to switch card power off\n" + "0x2: force to switch card power on", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x26, 0, 2, + }, + { + "cb_rst_n", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x27, 0, 1, + }, + { + "bios_spi_wp0_n", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x31, 0, 1, + }, + { + "bios_spi_wp1_n", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x31, 1, 1, + }, + { + "tlv_eeprom_wp", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x31, 2, 1, + }, + { + "sys_eeprom_wp", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x31, 3, 1, + }, + { + "fru_eeprom_wp", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x31, 4, 1, + }, + { + "psu_r_en", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x5f, 0, 1, + }, + { + "psu_l_en", + NULL, + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x5f, 1, 1, + }, + { + "psu_r_status", + "0x0: not OK\n" + "0x1: OK", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x60, 0, 1, + }, + { + "psu_l_status", + "0x0: not OK\n" + "0x1: OK", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x60, 1, 1, + }, + { + "psu_r_present", + "0x0: present\n" + "0x1: absent", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x60, 2, 1, + }, + { + "psu_l_present", + "0x0: present\n" + "0x1: absent", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x60, 3, 1, + }, + { + "psu_r_int_status", + "0x0: interrupt\n" + "0x1: no interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x60, 4, 1, + }, + { + "psu_l_int_status", + "0x0: interrupt\n" + "0x1: no interrupt", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x60, 5, 1, + }, + { + "psu_r_ac_status", + "0x0: not OK\n" + "0x1: OK", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x60, 6, 1, + }, + { + "psu_l_ac_status", + "0x0: not OK\n" + "0x1: OK", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x60, 7, 1, + }, + { + "psu_l_led_ctrl_en", + "0x0: disable\n" + "0x1: enable", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x61, 0, 1, + }, + { + "psu_r_led_ctrl_en", + "0x0: disable\n" + "0x1: enable", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x61, 1, 1, + }, + { + "sysled_ctrl", + "0x0: on\n" + "0x1: 1HZ blink\n" + "0x2: 4HZ blink\n" + "0x3: off", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x62, 0, 2, + }, + { + "sysled_select", + "0x0: green and yellow alternate blink\n" + "0x1: green\n" + "0x2: yellow\n" + "0x3: off", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x62, 4, 2, + }, + { + "fan_led_ctrl_en", + "0x0: disable\n" + "0x1: enable\n", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x63, 0, 1, + }, + { + "pwr_cycle", + "0x0: enable\n" + "0x1: disable", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x64, 0, 1, + }, + { + "bios_boot_ok", + "0x0: not ok\n" + "0x1: ok", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x70, 0, 1, + }, + { + "bios_boot_cs", + "0x0: from BIOS0\n" + "0x1: from BIOS1", + I2C_DEV_ATTR_SHOW_DEFAULT, + NULL, + 0x70, 1, 1, + }, + { + "boot_counter", + NULL, + NULL, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x71, 0, 8, + }, + { + "thermal_shutdown_en", + "0x0: disable\n" + "0x1: enable", + I2C_DEV_ATTR_SHOW_DEFAULT, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x75, 0, 1, + }, + { + "temp1_input", + "Switch chip Temperature", + switch_temp_show, + I2C_DEV_ATTR_STORE_DEFAULT, + 0x7A, 0, 8, + }, + { + "temp1_max", + "Switch chip Temperature", + sys_alarm_show, + sys_alarm_store, + SYSCPLD_ALARM_NODE, 0, 8, + }, + { + "temp1_max_hyst", + "Switch chip Temperature", + sys_alarm_show, + sys_alarm_store, + SYSCPLD_ALARM_NODE, 0, 8, + }, + { + "temp2_input", + "CPU chip Temperature", + sys_alarm_show, + sys_alarm_store, + SYSCPLD_ALARM_NODE, 0, 8, + }, + { + "temp2_max", + "CPU chip Temperature", + sys_alarm_show, + sys_alarm_store, + SYSCPLD_ALARM_NODE, 0, 8, + }, + { + "temp2_max_hyst", + "CPU chip Temperature", + sys_alarm_show, + sys_alarm_store, + SYSCPLD_ALARM_NODE, 0, 8, + }, +}; + +static int syscpld_remove(struct i2c_client *client) +{ + syscpld_client = NULL; + i2c_dev_sysfs_data_clean(client, &syscpld_data); + return 0; +} + +static int syscpld_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int n_attrs; + i2c_dev_attr_st *syscpld_attr_table; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA)) + return -ENODEV; + + syscpld_client = client; + //check board type then add different file nodes + board_type = i2c_smbus_read_byte_data(syscpld_client, 0x2); + + if (board_type < 0) { + dev_err(&client->dev, "Cannot read board type.\n"); + return board_type; + } else { + printk(KERN_INFO "Questone2bd CPLD driver loading\n"); + n_attrs = sizeof(syscpld_attr_table_questone2bd) / sizeof(syscpld_attr_table_questone2bd[0]); + syscpld_attr_table = syscpld_attr_table_questone2bd; + } + + return i2c_dev_sysfs_data_init(client, &syscpld_data, syscpld_attr_table, n_attrs); +} + +/* + * 0-present 1-power ok + * PSU1: present:bit2 power:bit0 + * PSU2: present:bit3 power:bit1 + * PSU1: 0x59 PSU2: 0x58 + * result: 0 is not OK, 1 is OK + */ +int psu_ok(int bus, unsigned short addr) +{ + int ret = 0; + i2c_dev_data_st *data; + int psu_status; + + if (syscpld_client == NULL) + return -ENODEV; + + data = i2c_get_clientdata(syscpld_client); + mutex_lock(&data->idd_lock); + + psu_status = i2c_smbus_read_byte_data(syscpld_client, 0x60); + if (addr == 0x59) { + if ((psu_status & 0x5) == 0x1) + ret = 1; + } else if (addr == 0x58) { + if ((psu_status & 0xa) == 0x2) + ret = 1; + } else { + ret = 0; + } + + mutex_unlock(&data->idd_lock); + return ret; +} +EXPORT_SYMBOL(psu_ok); + +static struct i2c_driver syscpld_driver = { + .driver = { + .name = "syscpld", + }, + .probe = syscpld_probe, + .remove = syscpld_remove, + .id_table = syscpld_id, +}; + +module_i2c_driver(syscpld_driver); + +MODULE_AUTHOR("Micky Zhan@Celestica.com"); +MODULE_DESCRIPTION("system CPLD driver for CPLD"); +MODULE_VERSION("0.0.1"); +MODULE_LICENSE("GPL"); + diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/scripts/get_fan_speed.sh b/platform/broadcom/sonic-platform-modules-cel/questone2bd/scripts/get_fan_speed.sh new file mode 100755 index 00000000000..db978690006 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/scripts/get_fan_speed.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# +# Copyright 2015-present Facebook. All Rights Reserved. +# +# This program file 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; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program in a file named COPYING; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301 USA +# + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin + +FAN_TOTAL=4 + +usage() { + echo "Usage: $0 [Fan Unit (1..$FAN_TOTAL)]" >&2 +} + +FAN_DIR=$(ls -d /sys/bus/i2c/drivers/fancpld/*-000d/) + +show_pwm() +{ + pwm="${FAN_DIR}/fan${1}_pwm" + val=$(cat $pwm | head -n 1) + if [ $((val * 100 % 255)) -ne 0 ]; then + echo "$((val * 100 / 255 + 1))%" + else + echo "$((val * 100 / 255))%" + fi +} + +show_rpm() +{ + front_rpm="${FAN_DIR}/fan$((($1 * 2 - 1)))_input" + rear_rpm="${FAN_DIR}/fan$((($1 * 2)))_input" + echo "$(cat $front_rpm), $(cat $rear_rpm)" +} + +set -e + +# refer to the comments in init_pwn.sh regarding +# the fan unit and tacho mapping +if [ "$#" -eq 0 ]; then + FANS="1 2 3 4" +elif [ "$#" -eq 1 ]; then + if [ $1 -gt $FAN_TOTAL ]; then + usage + exit 1 + fi + FANS="$1" +else + usage + exit 1 +fi + +for fan in $FANS; do + real_fan=$(($FAN_TOTAL-$fan+1)) + echo "Fan $fan RPMs: $(show_rpm $real_fan), ($(show_pwm $real_fan))" +done diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/scripts/set_fan_speed.sh b/platform/broadcom/sonic-platform-modules-cel/questone2bd/scripts/set_fan_speed.sh new file mode 100755 index 00000000000..825161335a5 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/scripts/set_fan_speed.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# +# Copyright 2015-present Facebook. All Rights Reserved. +# +# This program file 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; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program in a file named COPYING; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301 USA +# + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin + +FAN_TOTAL=4 + +usage() { + echo "Usage: $0 " >&2 +} + +FAN_DIR=$(ls -d /sys/bus/i2c/drivers/fancpld/*-000d/) + +set -e + +if [ "$#" -ne 2 ] && [ "$#" -ne 1 ]; then + usage + exit 1 +fi + +if [ "$#" -eq 1 ]; then + FANS="1 2 3 4" +else + if [ $2 -gt $FAN_TOTAL ]; then + usage + exit 1 + fi + FANS="$2" +fi + +# Convert the percentage to pwm +unit=$(( ( $1 * 255 ) / 100 )) + +for fan in $FANS; do + real_fan=$(($FAN_TOTAL-$fan+1)) + pwm="${FAN_DIR}/fan${real_fan}_pwm" + echo "$unit" > $pwm + echo "Successfully set fan ${fan} speed to $1%" +done diff --git a/platform/broadcom/sonic-platform-modules-cel/questone2bd/systemd/platform-modules-questone2bd.service b/platform/broadcom/sonic-platform-modules-cel/questone2bd/systemd/platform-modules-questone2bd.service new file mode 100644 index 00000000000..d81da0057f1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/questone2bd/systemd/platform-modules-questone2bd.service @@ -0,0 +1,14 @@ + +[Unit] +Description=Celestica questone2bd platform modules +After=local-fs.target +Before=pmon.service + +[Service] +Type=oneshot +ExecStart=-/etc/init.d/platform-modules-questone2bd start +ExecStop=-/etc/init.d/platform-modules-questone2bd stop +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/bmc_wdt/bmc_wdt.py b/platform/broadcom/sonic-platform-modules-cel/tools/bmc_wdt/bmc_wdt.py new file mode 100644 index 00000000000..e22d96326b7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/bmc_wdt/bmc_wdt.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python + +############################################################################# +# # +# Service to control CPU watchdog # +# # +############################################################################# + +import os +import time +import logging +import logging.handlers +import requests +import argparse +import subprocess + +HEARTBEAT_TIME = 20 +MAX_FILE_COUNT = 3 +WDT_TIMEOUT = 60 +MAX_LOG_BYTES = 20 * 1000000 +HOSTNAME = "240.1.1.1" +WDT_URL = "http://240.1.1.1:8080/api/sys/watchdog" +BMC_WDT_LOG = '/var/log/bmc_feed_watchdog.log' + + +lh = logging.handlers.RotatingFileHandler( + filename=BMC_WDT_LOG, maxBytes=MAX_LOG_BYTES, backupCount=MAX_FILE_COUNT) +formatter = logging.Formatter( + fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%b %d %H:%M:%S') +lh.setFormatter(formatter) +logger = logging.getLogger('bmc_feed_watchdog') +logger.addHandler(lh) +logger.setLevel(logging.INFO) + + +def set_wdt_timeout(timeout): + data = dict() + data["wdt"] = str(timeout) + status_code = -1 + message = None + try: + res = requests.post(WDT_URL, json=data, timeout=5) + status_code = res.status_code + message = res.json().get('result') + except: + message = "Unable set watchdog timeout" + + return status_code, message + + +def ping(): + try: + response = subprocess.check_output( + ['ping', '-c', '3', HOSTNAME], + stderr=subprocess.STDOUT, + universal_newlines=True + ) + except subprocess.CalledProcessError: + response = None + return response != None + + +def start(): + logger.info("Started CPU watchdog") + error_flag = 1 + status_code = -1 + while True: + status_code, message = set_wdt_timeout(WDT_TIMEOUT) + + # Error checking + if status_code == 200 and message != 'success': + logger.error(message) + error_flag = 1 + elif status_code != 200 and not ping(): + logger.error("Unable to connect to BMC") + error_flag = 1 + elif status_code != 200 and ping(): + if not error_flag: + logger.error(message) + time.sleep(1) + error_flag = 1 + continue + + # Pass error + if error_flag and status_code == 200 and message == 'success': + error_flag = 0 + logger.info("BMC connection successful") + + time.sleep(HEARTBEAT_TIME) + + +def stop(): + logger.info("Stopping CPU watchdog") + status_code = -1 + while status_code != 200: + status_code, message = set_wdt_timeout(0) + if status_code == 200 and message != 'success': + logger.error(message) + elif status_code != 200 and not ping(): + logger.error("Unable to connect to BMC") + elif ping(): + time.sleep(1) + continue + + logger.info("Stopped CPU watchdog") + + +def main(): + parser = argparse.ArgumentParser(description='') + parser.add_argument('option', choices=["start", "stop"]) + args = parser.parse_args() + if args.option == "start": + start() + stop() + + +if __name__ == "__main__": + main() diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/bmc_wdt/bmc_wdt.service b/platform/broadcom/sonic-platform-modules-cel/tools/bmc_wdt/bmc_wdt.service new file mode 100755 index 00000000000..88c3b5ceb44 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/bmc_wdt/bmc_wdt.service @@ -0,0 +1,10 @@ +[Unit] +Description=Service for enable BMC watchdog. +After=bmc_vlan.service + +[Service] +ExecStart=/usr/bin/python /usr/local/etc/bmc_wdt.py start +ExecStop=/usr/bin/python /usr/local/etc/bmc_wdt.py stop + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/bmcutil/bmc-exec b/platform/broadcom/sonic-platform-modules-cel/tools/bmcutil/bmc-exec new file mode 100755 index 00000000000..14435e857ff --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/bmcutil/bmc-exec @@ -0,0 +1,45 @@ +#!/bin/bash +# +# Copyright 2019-present Celestica. All Rights Reserved. +# +# This program file 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; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin + +command="$@" + +usage() { + echo "Usage: bmc-exec " + echo +} + +run_cmd() { + echo "Run command: "$command + echo + ret=$(curl -m 5 --silent --header "Content-Type:application/json" -d "{\"data\": \"${command}\"}" http://240.1.1.1:8080/api/sys/raw) + if [ -z "$ret" ]; + then + echo "Failed to connect on BMC" + else + echo $ret | python -c "import sys, json; k = json.load(sys.stdin)['result']; print k if type(k) is not list else '\n'.join(k);" + fi + return 0 +} + +if [ $# -lt 1 ]; then + usage + exit -1 +else + run_cmd +fi + +exit $? diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/bmcutil/bmcpwd b/platform/broadcom/sonic-platform-modules-cel/tools/bmcutil/bmcpwd new file mode 100644 index 00000000000..3e24c6b1353 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/bmcutil/bmcpwd @@ -0,0 +1 @@ +kt3I0K_QxQ== \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/bmcutil/bmcutil.py b/platform/broadcom/sonic-platform-modules-cel/tools/bmcutil/bmcutil.py new file mode 100644 index 00000000000..7edc55ac886 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/bmcutil/bmcutil.py @@ -0,0 +1,296 @@ +#!/usr/bin/python + +""" +bmcutil.py +BMC utility, implements management functions provided by BMC RESTful APIs. +""" + +import requests +import re +import hashlib +import binascii +import os +import base64 + + +# Base class of BmcUtil +class BmcUtilBase(object): + def __init__(self): + self.bmc_info_url = "http://240.1.1.1:8080/api/sys/bmc" + self.bmc_eth_info_url = "http://240.1.1.1:8080/api/sys/eth" + self.bmc_raw_command_url = "http://240.1.1.1:8080/api/sys/raw" + self.bmc_pwd_url = "http://240.1.1.1:8080/api/sys/userpassword" + self.bmc_pwd_path = "/usr/local/etc/bmcpwd" + self.bmc_syslog_url = "http://240.1.1.1:8080/api/sys/syslog" + + def request_data(self, url): + # Reqest data from BMC if not exist. + data_req = requests.get(url) + data_json = data_req.json() + data_list = data_json.get('Information') + return data_list + + def save_bmc_password(self, clear_pwd): + enc = [] + key = "bmc" + for i in range(len(clear_pwd)): + key_c = key[i % len(key)] + enc_c = chr((ord(clear_pwd[i]) + ord(key_c)) % 256) + enc.append(enc_c) + enc_pwd = base64.urlsafe_b64encode("".join(enc)) + + with open(self.bmc_pwd_path, 'w') as file: + file.write(enc_pwd) + + def get_bmc_pass(self): + with open(self.bmc_pwd_path) as file: + data = file.read() + + key = "bmc" + dec = [] + enc = base64.urlsafe_b64decode(data) + for i in range(len(enc)): + key_c = key[i % len(key)] + dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256) + dec.append(dec_c) + return "".join(dec) + + def version(self): + """ + Return version information string + @return version string of BMC OS + """ + bmc_version = None + + bmc_version_key = "OpenBMC Version" + bmc_info = self.request_data(self.bmc_info_url) + bmc_version = bmc_info.get(bmc_version_key) + + return str(bmc_version) + + def set_eth0_addr(self, ip_addr, mask): + """ + Set eth0 IPv4 address + @ip_addr MANDATORY, IPv4 ip address string + @mask MANDATORY, IPv4 network mask string + """ + + json_data = dict() + json_data["data"] = "ifconfig eth0 %s netmask %s up" % (ip_addr, mask) + r = requests.post(self.bmc_raw_command_url, json=json_data) + if r.status_code != 200: + return False + return True + + def get_eth0_addr_list(self): + """ + Get eth0 IPv4 address + @return a list of (IPv4 ip address/mask string) + """ + ipv4_adress = [] + eth_data_list = self.request_data(self.bmc_eth_info_url) + + for eth_data in eth_data_list: + if 'inet addr' in eth_data: + ipv4_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', eth_data) + if len(ipv4_list) == 3: + ipv4 = ipv4_list[0] + "/" + ipv4_list[2] + ipv4_adress.append(ipv4) + + return str(ipv4_adress) + + def set_gateway_ip(self, gw_ip): + """ + Set gateway IPv4 address string + @gw_ip MANATORY, IPv4 address of gateway + """ + + json_data = dict() + json_data["data"] = "route del default" + + r = requests.post(self.bmc_raw_command_url, json=json_data) + if r.status_code != 200: + return False + + json_data["data"] = "route add default gw %s" % gw_ip + r = requests.post(self.bmc_raw_command_url, json=json_data) + if r.status_code != 200: + return False + + return True + + def get_gateway_ip(self): + """ + Get gateway IPv4 address string + @return IPv4 address of gateway + """ + + default_gw = None + + json_data = dict() + json_data["data"] = "route" + + r = requests.post(self.bmc_raw_command_url, json=json_data) + if r.status_code == 200: + data_list = r.json().get('result') + for raw_data in data_list: + if 'default' in raw_data: + route_data = raw_data.split() + default_gw = route_data[1] if len(route_data) > 0 else None + + return str(default_gw) + + def set_user_and_passwd(self, user_name, password): + """ + Set BMC user name and password + @user_name MANDATORY, BMC user + @password MANDATORY, BMC user's password + """ + json_data = dict() + json_data["user"] = str(user_name) + json_data["oldpassword"] = self.get_bmc_pass() + json_data["newpassword"] = password + r = requests.post(self.bmc_pwd_url, json=json_data) + return_data = r.json() + + if r.status_code != 200 or 'success' not in return_data.get('result'): + return False + + self.save_bmc_password(password) + return True + + def add_syslog_server(self, svr_ip, svr_port): + """ + Add syslog server for BMC + @svr_ip MANDATORY, syslog server IP string + @svr_port MANDATORY, syslog server destination port + """ + json_data = dict() + json_data["addr"] = str(svr_ip) + json_data["port"] = str(svr_port) + r = requests.post(self.bmc_syslog_url, json=json_data) + if r.status_code != 200 or 'success' not in r.json().get('result'): + return False + return True + + def get_syslog_server_list(self): + """ + # Get syslog server list of BMC + # @return a list of syslog server ip and destination port pair + """ + syslog_ip = None + syslog_port = None + + json_data = dict() + json_data["data"] = "tail -n 1 /etc/rsyslog.conf" + r = requests.post(self.bmc_raw_command_url, json=json_data) + if r.status_code != 200: + return False + + return_data = r.json() + result = return_data.get("result") + ip = re.findall(r'[0-9]+(?:\.[0-9]+){3}', result[0]) + port = str(result[0]).split(":") + syslog_ip = ip[0] if len(ip) > 0 else None + syslog_port = port[1] if len(port) > 1 else None + + return [syslog_ip, syslog_port] + + def del_syslog_server(self, svr_ip, svr_port): + """ + Delete syslog server for BMC + @svr_ip MANDATORY, syslog server IP string + @svr_port MANDATORY, syslog server destination port + """ + json_data = dict() + json_data["addr"] = "127.0.0.1" + json_data["port"] = str(svr_port) + r = requests.post(self.bmc_syslog_url, json=json_data) + if r.status_code != 200 or 'success' not in r.json().get('result'): + return False + return True + + def get_bmc_system_state(self): + """ + Get BMC system state, includes CPU, memory, storage + MUST contains status of: CPU, memory, disk + dict object: + { + "CPU": { + "StateOutputs": "output of command 'top -bn 1'" + "Usage": "10.0" + }, + "MEMORY": { + "StateOutputs": "output of command 'free -m'" + "Usage": "15.0" # caculate: "free -t | grep \"buffers/cache\" | awk '{ printf \"mem usage : %.1f%%\\n\",$3/($3+$4) * 100}'" + }, + "DISK": { + "StateOutput": "output of command 'df -h'" + "Usage": "12.5" + } + } + """ + + state_data = dict() + bmc_info = self.request_data(self.bmc_info_url) + + cpu_key = "CPU Usage" + cpu_data_raw = bmc_info.get(cpu_key) + cpu_usage = cpu_data_raw.split()[1].strip('%') + cpu_data = dict() + cpu_data["StateOutputs"] = "output of command 'top -bn 1'" + cpu_data["Usage"] = "{:.1f}".format(float(cpu_usage)) + state_data["CPU"] = cpu_data + + disk_key = "Disk Usage" + disk_data_raw = bmc_info.get(disk_key) + disk_usage = disk_data_raw.split()[7].strip('%') + disk_data = dict() + disk_data["StateOutputs"] = "output of command 'df -h'" + disk_data["Usage"] = "{:.1f}".format(float(disk_usage)) + state_data["DISK"] = disk_data + + json_data = dict() + json_data["data"] = "free -t" + mem_usage = "None" + r = requests.post(self.bmc_raw_command_url, json=json_data) + if r.status_code == 200: + mem_data_raw = r.json().get('result')[2] + mem_u = float(mem_data_raw.split()[2]) + mem_f = float(mem_data_raw.split()[3]) + mem_usage = (mem_u/(mem_u+mem_f)) * 100 + mem_data = dict() + mem_data["StateOutputs"] = "output of command 'free -t'" + mem_data["Usage"] = "{:.1f}".format(mem_usage) + state_data["MEMORY"] = mem_data + + return state_data + + def reboot_bmc(self): + """ + Reboot BMC + """ + json_data = dict() + json_data["data"] = "reboot" + r = requests.post(self.bmc_raw_command_url, json=json_data) + if r.status_code != 200: + return False + + return True + + def set_location_led(self, admin_state): + """ + Enable/disable location LED + @admin_state MANDATORY, should be string "on" or "off" + """ + + json_data = dict() + if str(admin_state).lower() not in ["on", "off"]: + return False + + json_data["data"] = "led_location.sh %s" % admin_state + r = requests.post(self.bmc_raw_command_url, json=json_data) + if r.status_code != 200: + return False + + return True diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/fanctrl/Makefile b/platform/broadcom/sonic-platform-modules-cel/tools/fanctrl/Makefile new file mode 100644 index 00000000000..fa993b038c1 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/fanctrl/Makefile @@ -0,0 +1,26 @@ +# Copyright 2016-present Celestica. All Rights Reserved. +# +# This program file 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; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program in a file named COPYING; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301 USA + +defaults: fand_v2 + +fand_v2: fand_v2.c + $(CXX) $(CXXFLAGS) -pthread -o $@ $^ $(LDFLAGS) + +.PHONY: clean + +clean: + rm -rf *.o fand_v2 diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/fanctrl/fanctrl.service b/platform/broadcom/sonic-platform-modules-cel/tools/fanctrl/fanctrl.service new file mode 100644 index 00000000000..f893f8c3708 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/fanctrl/fanctrl.service @@ -0,0 +1,10 @@ +[Unit] +Description=Service for fancontrol +After=platform-modules-questone2bd.service + +[Service] +Type=forking +ExecStart=/usr/local/bin/fand_v2 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/fanctrl/fand_v2.c b/platform/broadcom/sonic-platform-modules-cel/tools/fanctrl/fand_v2.c new file mode 100644 index 00000000000..af6a2988a23 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/fanctrl/fand_v2.c @@ -0,0 +1,2533 @@ +/* + * fand + * + * Copyright 2016-present Celestica. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Daemon to manage the fan speed to ensure that we stay within a reasonable + * temperature range. We're using a simplistic algorithm to get started: + * + * If the fan is already on high, we'll move it to medium if we fall below + * a top temperature. If we're on medium, we'll move it to high + * if the temperature goes over the top value, and to low if the + * temperature falls to a bottom level. If the fan is on low, + * we'll increase the speed if the temperature rises to the top level. + * + * To ensure that we're not just turning the fans up, then back down again, + * we'll require an extra few degrees of temperature drop before we lower + * the fan speed. + * + * We check the RPM of the fans against the requested RPMs to determine + * whether the fans are failing, in which case we'll turn up all of + * the other fans and report the problem.. + * + * TODO: Implement a PID algorithm to closely track the ideal temperature. + * TODO: Determine if the daemon is already started. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fand_v2.h" + +//#define DEBUG +//#define FOR_F2B +//#define FANCTRL_SIMULATION 1 +//#define CONFIG_PSU_FAN_CONTROL_INDEPENDENT 1 +#define CONFIG_FSC_CONTROL_PID 1 //for PID control + +#define TOTAL_FANS 4 +#define TOTAL_PSUS 2 +#define FAN_MEDIUM 128 +#define FAN_HIGH 100 +#define FAN_MAX 255 +#define FAN_MIN 76 +#define FAN_NORMAL_MAX 178 +#define FAN_NORMAL_MIN FAN_MIN +#define FAN_ONE_FAIL_MAX FAN_MAX +#define FAN_ONE_FAIL_MIN 153 +#define RAISING_TEMP_LOW 25 +#define RAISING_TEMP_HIGH 40 +#define FALLING_TEMP_LOW 23 +#define FALLING_TEMP_HIGH 40 +#define SYSTEM_LIMIT 80 + +#define ALARM_TEMP_THRESHOLD 1 +#define ALARM_START_REPORT 3 +#define WARN_RECOVERY_COUNT 90 //remain 5 minutes to recovery normal speed + +#define CRITICAL_TEMP_HYST 2 + +#define REPORT_TEMP 720 /* Report temp every so many cycles */ +#define FAN_FAILURE_OFFSET 30 +#define FAN_FAILURE_THRESHOLD 3 /* How many times can a fan fail */ +#define SYS_FAN_LED_PATH "/sys/bus/i2c/drivers/syscpld/70-000d/fan_led_ctrl_en" +#define SYS_FAN_LED_GREEN 0 +#define SYS_FAN_LED_RED 1 +#define FAN_LED_GREEN 1 +#define FAN_LED_RED 2 +#define PSU_LED_GREEN 0 +#define PSU_LED_RED 1 +#define SHUTDOWN_DELAY_TIME 72 /*if trigger shutdown event, delay 6 minutes to shutdown */ + +#define BAD_TEMP (-60) +#define ERROR_TEMP_MAX 5 + +#define FAN_FAIL_COUNT 9 +#define FAN_FAIL_RPM 1000 +#define FAN_FRONTT_SPEED_MAX 24150 +#define FAN_REAR_SPEED_MAX 28950 +#define PSU_SPEED_MAX 26496 + +#define FAN_DIR_INIT -1 +#define FAN_DIR_FAULT 0 +#define FAN_DIR_B2F 1 +#define FAN_DIR_F2B 2 +#define THERMAL_DIR_F2B_STR "R1241-F0001" +#define THERMAL_DIR_B2F_STR "R1241-F0002" +#define FAN_DIR_F2B_STR "R1241-F9001" +#define FAN_DIR_B2F_STR "R1241-F9002" +#define DELTA_PSU_DIR_F2B_STR "DPS-1100FB" +#define DELTA_PSU_DIR_B2F_STR "DPS-1100AB" +#define ACBEL_PSU_DIR_F2B_STR "FSJ026-A20G" +#define ACBEL_PSU_DIR_B2F_STR "FSJ038-A20G" + +#define PWM_UNIT_MAX 255 + +#define FAN_WDT_TIME (0x3c) //5 * 60 +#define FAN_WDT_ENABLE_SYSFS "/sys/bus/i2c/drivers/fancpld/66-000d/wdt_en" +#define FAN_WDT_TIME_SYSFS "/sys/bus/i2c/drivers/fancpld/66-000d/wdt_time" +#define PSU1_SHUTDOWN_SYSFS "/sys/bus/i2c/drivers/dps1100/76-0059/control/shutdown" +#define PSU2_SHUTDOWN_SYSFS "/sys/bus/i2c/drivers/dps1100/75-0058/control/shutdown" +#define PSU_SPEED_CTRL_NODE "fan1_cfg" +#define PSU_SPEED_CTRL_ENABLE 0x90 + +#define PID_CONFIG_PATH "/usr/local/etc/pid_config_questone2bd.ini" +#define PID_FILE_LINE_MAX 100 + +#define DISABLE 0 +#define LOW_WARN_BIT (0x1 << 0) +#define HIGH_WARN_BIT (0x1 << 1) +#define PID_CTRL_BIT (0x1 << 2) +#define SWITCH_SENSOR_BIT (0x1 << 3) +#define CRITICAL_SENSOR_BIT (0x1 << 4) +#define HIGH_MAX_BIT (0x1 << 5) + +#define NORMAL_K ((float)(FAN_NORMAL_MAX - FAN_NORMAL_MIN) / (RAISING_TEMP_HIGH - RAISING_TEMP_LOW)) +#define ONE_FAIL_K ((float)(FAN_ONE_FAIL_MAX - FAN_ONE_FAIL_MIN) / (RAISING_TEMP_HIGH - RAISING_TEMP_LOW)) + +static int calculate_fan_normal_pwm(int cur_temp, int last_temp); +static int calculate_fan_one_fail_pwm(int cur_temp, int last_temp); + +static int read_temp_sysfs(struct sensor_info_sysfs *sensor); +static int read_temp_directly_sysfs(struct sensor_info_sysfs *sensor); + + +struct line_policy fishbone48_f2b_normal = { + .temp_hyst = CRITICAL_TEMP_HYST, + .begin = { + .temp = RAISING_TEMP_LOW, + .speed = FAN_NORMAL_MIN, + }, + .end = { + .temp = RAISING_TEMP_HIGH, + .speed = FAN_NORMAL_MAX, + }, + .get_speed = calculate_line_speed, +}; + +struct line_policy fishbone48_f2b_onefail = { + .temp_hyst = CRITICAL_TEMP_HYST, + .begin = { + .temp = RAISING_TEMP_LOW, + .speed = FAN_ONE_FAIL_MIN, + }, + .end = { + .temp = RAISING_TEMP_HIGH, + .speed = FAN_ONE_FAIL_MAX, + }, + .get_speed = calculate_line_speed, +}; + +struct line_policy fishbone48_b2f_normal = { + .temp_hyst = CRITICAL_TEMP_HYST, + .begin = { + .temp = RAISING_TEMP_LOW, + .speed = FAN_NORMAL_MIN, + }, + .end = { + .temp = RAISING_TEMP_HIGH, + .speed = FAN_NORMAL_MAX, + }, + .get_speed = calculate_line_speed, +}; + +struct line_policy fishbone48_b2f_onefail = { + .temp_hyst = CRITICAL_TEMP_HYST, + .begin = { + .temp = RAISING_TEMP_LOW, + .speed = FAN_ONE_FAIL_MIN, + }, + .end = { + .temp = RAISING_TEMP_HIGH, + .speed = FAN_ONE_FAIL_MAX, + }, + .get_speed = calculate_line_speed, +}; + +/* Fan EEPROM Path: Index is matters + * See fantray_info[] + */ +const char *fan_eeprom_path[] = { + /* FAN tray EEPROM */ + "/sys/bus/i2c/devices/2-0050/eeprom", + "/sys/bus/i2c/devices/4-0050/eeprom", + "/sys/bus/i2c/devices/6-0050/eeprom", + "/sys/bus/i2c/devices/8-0050/eeprom", + /* PSU EEPROM */ + "/sys/bus/i2c/devices/76-0051/eeprom", + "/sys/bus/i2c/devices/75-0050/eeprom", +}; + +static struct sensor_info_sysfs sensor_inlet_u52_critical_info = { + .prefix = "/sys/bus/i2c/drivers/lm75/9-0048", + .suffix = "temp1_input", + .error_cnt = 0, + .temp = 0, + .t1 = 0, + .t2 = 0, + .old_pwm = 0, + .setpoint = 0, + .p = 0, + .i = 0, + .d = 0, + .min_output = FAN_MIN, + .max_output = FAN_MAX, + .read_sysfs = &read_temp_sysfs, +}; + +static struct sensor_info_sysfs sensor_inlet_u28_critical_info = { + .prefix = "/sys/bus/i2c/drivers/lm75/67-004d", + .suffix = "temp1_input", + .error_cnt = 0, + .temp = 0, + .t1 = 0, + .t2 = 0, + .old_pwm = 0, + .setpoint = 0, + .p = 0, + .i = 0, + .d = 0, + .min_output = FAN_MIN, + .max_output = FAN_MAX, + .read_sysfs = &read_temp_sysfs, +}; + +static struct sensor_info_sysfs sensor_bcm5870_inlet_critical_info_f2b = { + .prefix = "/sys/bus/i2c/drivers/syscpld/70-000d", + .suffix = "temp1_input", + .error_cnt = 0, + .temp = 0, + .t1 = 0, + .t2 = 0, + .old_pwm = 0, + .setpoint = 95, + .p = 3, + .i = 0.3, + .d = 0.3, + .min_output = FAN_MIN, + .max_output = FAN_MAX, + .read_sysfs = &read_temp_directly_sysfs, +}; + +static struct sensor_info_sysfs sensor_bcm5870_inlet_critical_info_b2f = { + .prefix = "/sys/bus/i2c/drivers/syscpld/70-000d", + .suffix = "temp1_input", + .error_cnt = 0, + .temp = 0, + .t1 = 0, + .t2 = 0, + .old_pwm = 0, + .setpoint = 95, + .p = 3, + .i = 0.5, + .d = 0.5, + .min_output = FAN_MIN, + .max_output = FAN_MAX, + .read_sysfs = &read_temp_directly_sysfs, +}; + +static struct sensor_info_sysfs sensor_cpu_inlet_critical_info_f2b = { + .prefix = "/sys/bus/i2c/drivers/syscpld/70-000d", + .suffix = "temp2_input", + .error_cnt = 0, + .temp = 0, + .t1 = 0, + .t2 = 0, + .old_pwm = 0, + .setpoint = -15, + .p = 2, + .i = 0.5, + .d = 0.5, + .min_output = FAN_MIN, + .max_output = FAN_MAX, + .read_sysfs = &read_temp_directly_sysfs, +}; + +static struct sensor_info_sysfs sensor_cpu_inlet_critical_info_b2f = { + .prefix = "/sys/bus/i2c/drivers/syscpld/70-000d", + .suffix = "temp2_input", + .error_cnt = 0, + .temp = 0, + .t1 = 0, + .t2 = 0, + .old_pwm = 0, + .setpoint = -15, + .p = 2, + .i = 0.3, + .d = 0.3, + .min_output = FAN_MIN, + .max_output = FAN_MAX, + .read_sysfs = &read_temp_directly_sysfs, +}; + + +/* fantray info*/ +static struct fan_info_stu_sysfs fan4_info = { + .prefix = "/sys/bus/i2c/drivers/fancpld/66-000d", + .front_fan_prefix = "fan1_input", + .rear_fan_prefix = "fan2_input", + .pwm_prefix = "fan1_pwm", + .fan_led_prefix = "fan1_led", + .fan_present_prefix = "fan1_present", + .fan_status_prefix = NULL, + //.present = 1, + .front_failed = 0, + .rear_failed = 0, +}; + +static struct fan_info_stu_sysfs fan3_info = { + .prefix = "/sys/bus/i2c/drivers/fancpld/66-000d", + .front_fan_prefix = "fan3_input", + .rear_fan_prefix = "fan4_input", + .pwm_prefix = "fan2_pwm", + .fan_led_prefix = "fan2_led", + .fan_present_prefix = "fan2_present", + .fan_status_prefix = NULL, + //.present = 1, + .front_failed = 0, + .rear_failed = 0, +}; + +static struct fan_info_stu_sysfs fan2_info = { + .prefix = "/sys/bus/i2c/drivers/fancpld/66-000d", + .front_fan_prefix = "fan5_input", + .rear_fan_prefix = "fan6_input", + .pwm_prefix = "fan3_pwm", + .fan_led_prefix = "fan3_led", + .fan_present_prefix = "fan3_present", + .fan_status_prefix = NULL, + //.present = 1, + .front_failed = 0, + .rear_failed = 0, +}; + +static struct fan_info_stu_sysfs fan1_info = { + .prefix = "/sys/bus/i2c/drivers/fancpld/66-000d", + .front_fan_prefix = "fan7_input", + .rear_fan_prefix = "fan8_input", + .pwm_prefix = "fan4_pwm", + .fan_led_prefix = "fan4_led", + .fan_present_prefix = "fan4_present", + .fan_status_prefix = NULL, + //.present = 1, + .front_failed = 0, + .rear_failed = 0, +}; + +static struct fan_info_stu_sysfs psu2_fan_info = { + .prefix = "/sys/bus/i2c/drivers/syscpld/70-000d", + .front_fan_prefix = "fan1_input", + .rear_fan_prefix = "/sys/bus/i2c/drivers/dps1100/75-0058", + .pwm_prefix = "fan1_pct", + .fan_led_prefix = "psu_l_led_ctrl_en", + .fan_present_prefix = "psu_l_present", + .fan_status_prefix = "psu_l_status", + //.present = 1, + .front_failed = 0, + .rear_failed = 0, +}; + +static struct fan_info_stu_sysfs psu1_fan_info = { + .prefix = "/sys/bus/i2c/drivers/syscpld/70-000d", + .front_fan_prefix = "fan1_input", + .rear_fan_prefix = "/sys/bus/i2c/drivers/dps1100/76-0059", + .pwm_prefix = "fan1_pct", + .fan_led_prefix = "psu_r_led_ctrl_en", + .fan_present_prefix = "psu_r_present", + .fan_status_prefix = "psu_r_status", + //.present = 1, + .front_failed = 0, + .rear_failed = 0, +}; + + + + +/************board and fantray info*****************/ +static struct board_info_stu_sysfs board_info[] = { + /*B2F*/ + { + .name = "INLET_TEMP", + .slot_id = FAN_DIR_B2F, + .correction = -1, + .lwarn = 40, + .hwarn = 43, + .warn_count = 0, + .recovery_count = 0, + .flag = CRITICAL_SENSOR_BIT, + .critical = &sensor_inlet_u52_critical_info, + .alarm = &sensor_inlet_u52_critical_info, + }, +#ifdef CONFIG_FSC_CONTROL_PID + { + .name = "SWITCH_TEMP", + .slot_id = FAN_DIR_B2F, + .correction = 15, + .lwarn = 108, + .hwarn = 112, + .warn_count = 0, + .recovery_count = 0, + .flag = PID_CTRL_BIT, + .critical = &sensor_bcm5870_inlet_critical_info_b2f, + .alarm = &sensor_bcm5870_inlet_critical_info_b2f, + }, + { + .name = "CPU_TEMP", + .slot_id = FAN_DIR_B2F, + .correction = -104, + .lwarn = -3, + .hwarn = -1, + .warn_count = 0, + .recovery_count = 0, + .flag = PID_CTRL_BIT,//PID_CTRL_BIT, + .critical = &sensor_cpu_inlet_critical_info_b2f, + .alarm = &sensor_cpu_inlet_critical_info_b2f, + }, +#endif + + /*F2B*/ + { + .name = "INLET_TEMP", + .slot_id = FAN_DIR_F2B, + .correction = -6, + .lwarn = 40, + .hwarn = 43, + .warn_count = 0, + .recovery_count = 0, + .flag = CRITICAL_SENSOR_BIT, + .critical = &sensor_inlet_u28_critical_info, + .alarm = &sensor_inlet_u28_critical_info, + }, +#ifdef CONFIG_FSC_CONTROL_PID + { + .name = "SWITCH_TEMP", + .slot_id = FAN_DIR_F2B, + .correction = 15, + .lwarn = 108, + .hwarn = 112, + .warn_count = 0, + .recovery_count = 0, + .flag = PID_CTRL_BIT, + .critical = &sensor_bcm5870_inlet_critical_info_f2b, + .alarm = &sensor_bcm5870_inlet_critical_info_f2b, + }, + { + .name = "CPU_TEMP", + .slot_id = FAN_DIR_F2B, + .correction = -104, + .lwarn = -3, + .hwarn = -1, + .warn_count = 0, + .recovery_count = 0, + .flag = PID_CTRL_BIT,//PID_CTRL_BIT, + .critical = &sensor_cpu_inlet_critical_info_f2b, + .alarm = &sensor_cpu_inlet_critical_info_f2b, + }, +#endif + NULL, +}; + +static struct fantray_info_stu_sysfs fantray_info[] = { + { + .name = "FAN1", + .present = 1, + .read_eeprom = 1, + .status = 1, + .failed = 0, + .direction = FAN_DIR_INIT, + .eeprom_fail = 0, + .fan1 = fan1_info, + }, + { + .name = "FAN2", + .present = 1, + .read_eeprom = 1, + .status = 1, + .failed = 0, + .direction = FAN_DIR_INIT, + .eeprom_fail = 0, + .fan1 = fan2_info, + }, + { + .name = "FAN3", + .present = 1, + .read_eeprom = 1, + .status = 1, + .failed = 0, + .direction = FAN_DIR_INIT, + .eeprom_fail = 0, + .fan1 = fan3_info, + }, + { + .name = "FAN4", + .present = 1, + .read_eeprom = 1, + .status = 1, + .failed = 0, + .direction = FAN_DIR_INIT, + .eeprom_fail = 0, + .fan1 = fan4_info, + }, + { + .name = "PSU1", + .present = 1, + .read_eeprom = 1, + .status = 1, + .failed = 0, + .direction = FAN_DIR_INIT, + .eeprom_fail = 0, + .fan1 = psu1_fan_info, + }, + { + .name = "PSU2", + .present = 1, + .read_eeprom = 1, + .status = 1, + .failed = 0, + .direction = FAN_DIR_INIT, + .eeprom_fail = 0, + .fan1 = psu2_fan_info, + }, + NULL, +}; + +#define BOARD_INFO_SIZE (sizeof(board_info) \ + / sizeof(struct board_info_stu_sysfs)) +#define FANTRAY_INFO_SIZE (sizeof(fantray_info) \ + / sizeof(struct fantray_info_stu_sysfs)) + +struct rpm_to_pct_map rpm_front_map[] = {{20, 4950}, + {25, 6150}, + {30, 7500}, + {35, 8700}, + {40, 9900}, + {45, 11250}, + {50, 12300}, + {55, 13650}, + {60, 14850}, + {65, 16050}, + {70, 17400}, + {75, 18600}, + {80, 19950}, + {85, 21000}, + {90, 22350}, + {95, 23550}, + {100, 24150} +}; + +struct rpm_to_pct_map rpm_rear_map[] = {{20, 6000}, + {25, 7500}, + {30, 8850}, + {35, 10500}, + {40, 12150}, + {45, 13350}, + {50, 14850}, + {55, 16650}, + {60, 18000}, + {65, 19350}, + {70, 20850}, + {75, 22350}, + {80, 24000}, + {85, 25350}, + {90, 26850}, + {95, 28350}, + {100, 28950} +}; + +struct rpm_to_pct_map psu_rpm_map[] = {{20, 8800}, + {25, 8800}, + {30, 8800}, + {35, 8800}, + {40, 8800}, + {45, 9920}, + {50, 11520}, + {55, 13120}, + {60, 14560}, + {65, 16192}, + {70, 17760}, + {75, 19296}, + {80, 20800}, + {85, 21760}, + {90, 23424}, + {95, 24800}, + {100, 26496} +}; + +#define FRONT_MAP_SIZE (sizeof(rpm_front_map) / sizeof(struct rpm_to_pct_map)) +#define REAR_MAP_SIZE (sizeof(rpm_rear_map) / sizeof(struct rpm_to_pct_map)) +#define PSU_MAP_SIZE (sizeof(psu_rpm_map) / sizeof(struct rpm_to_pct_map)) + +static struct thermal_policy f2b_normal_policy = { + .pwm = FAN_NORMAL_MIN, + .old_pwm = FAN_NORMAL_MIN, + .line = &fishbone48_f2b_normal, +}; + +static struct thermal_policy f2b_one_fail_policy = { + .pwm = FAN_ONE_FAIL_MIN, + .old_pwm = FAN_ONE_FAIL_MIN, + .line = &fishbone48_f2b_onefail, +}; + +static struct thermal_policy b2f_normal_policy = { + .pwm = FAN_NORMAL_MIN, + .old_pwm = FAN_NORMAL_MIN, + .line = &fishbone48_b2f_normal, +}; + +static struct thermal_policy b2f_one_fail_policy = { + .pwm = FAN_ONE_FAIL_MIN, + .old_pwm = FAN_ONE_FAIL_MIN, + .line = &fishbone48_b2f_onefail, +}; + +static struct thermal_policy *policy = NULL; +static int pid_using = 0; +static int direction = FAN_DIR_INIT; +static int sys_fan_led_color = 0; +static int psu_led_color = 0; +static int fan_speed_temp = FAN_MEDIUM; + +static int write_fan_led(const int fan, const int color); +static int write_fan_speed(const int fan, const int value); +static int write_psu_fan_speed(const int fan, int value); + +/* + * Initialize path cache by writing 0-length string + */ +static int init_path_cache(void) +{ + int i = 0; + // Temp Sensor datastructure + for (i = 0; i < BOARD_INFO_SIZE; i++) + { + if (board_info[i].alarm != NULL) + snprintf(board_info[i].alarm->path_cache, PATH_CACHE_SIZE, ""); + if (board_info[i].critical != NULL) + snprintf(board_info[i].critical->path_cache, PATH_CACHE_SIZE, ""); + } + + return 0; +} + +/* + * Helper function to probe directory, and make full path + * Will probe directory structure, then make a full path + * using "/hwmon/hwmonxxx/" + * returns < 0, if hwmon directory does not exist or something goes wrong + */ +int assemble_sysfs_path(const char* prefix, const char* suffix, + char* full_name, int buffer_size) +{ + int rc = 0; + int dirname_found = 0; + char temp_str[PATH_CACHE_SIZE]; + DIR *dir = NULL; + struct dirent *ent; + + if (full_name == NULL) + return -1; + + snprintf(temp_str, (buffer_size - 1), "%s/hwmon", prefix); + dir = opendir(temp_str); + if (dir == NULL) { + rc = ENOENT; + goto close_dir_out; + } + + while ((ent = readdir(dir)) != NULL) { + if (strstr(ent->d_name, "hwmon")) { + // found the correct 'hwmon??' directory + snprintf(full_name, buffer_size, "%s/%s/%s", + temp_str, ent->d_name, suffix); + dirname_found = 1; + break; + } + } + +close_dir_out: + if (dir != NULL) { + closedir(dir); + } + + if (dirname_found == 0) { + rc = ENOENT; + } + + return rc; +} + +static int adjust_sysnode_path(const char* prefix, const char* suffix, + char* full_name, int buffer_size) +{ + int rc = 0; + FILE *fp; + int dirname_found = 0; + char temp_str[PATH_CACHE_SIZE]; + DIR *dir = NULL; + struct dirent *ent; + + if (full_name == NULL) + return -1; + snprintf(temp_str, (buffer_size - 1), "%s/%s", prefix, suffix); + fp = fopen(temp_str, "r"); + if (fp) { + fclose(fp); + return 0; + } + + /*adjust the path, because the hwmon id may be changed*/ + snprintf(temp_str, (buffer_size - 1), "%s/hwmon", prefix); + dir = opendir(temp_str); + if (dir == NULL) { + rc = ENOENT; + goto close_dir_out; + } + + while ((ent = readdir(dir)) != NULL) { + if (strstr(ent->d_name, "hwmon")) { + // found the correct 'hwmon??' directory + snprintf(full_name, buffer_size, "%s/%s/%s", + temp_str, ent->d_name, suffix); + dirname_found = 1; + break; + } + } + +close_dir_out: + if (dir != NULL) { + closedir(dir); + } + + if (dirname_found == 0) { + rc = ENOENT; + } + + return rc; + +} + +// Functions for reading from sysfs stub +static int read_sysfs_raw_internal(const char *device, char *value, int log) +{ + FILE *fp; + int rc, err; + + fp = fopen(device, "r"); + if (!fp) { + if (log) { + err = errno; + syslog(LOG_ERR, "failed to open device %s for read: %s", + device, strerror(err)); + errno = err; + } + return -1; + } + + rc = fscanf(fp, "%s", value); + fclose(fp); + + if (rc != 1) { + if (log) { + err = errno; + syslog(LOG_ERR, "failed to read device %s: %s", + device, strerror(err)); + errno = err; + } + return -1; + } + + return 0; +} + +static int read_sysfs_raw(char *sysfs_path, char *buffer) +{ + return read_sysfs_raw_internal(sysfs_path, buffer, 1); +} + +// Returns 0 for success, or -1 on failures. +static int read_sysfs_int(char *sysfs_path, int *buffer) +{ + int rc; + char readBuf[PATH_CACHE_SIZE]; + + if (sysfs_path == NULL || buffer == NULL) { + errno = EINVAL; + return -1; + } + + rc = read_sysfs_raw(sysfs_path, readBuf); + if (rc == 0) + { + if (strstr(readBuf, "0x") || strstr(readBuf, "0X")) + sscanf(readBuf, "%x", buffer); + else + sscanf(readBuf, "%d", buffer); + } + return rc; +} + +static int write_sysfs_raw_internal(const char *device, char *value, int log) +{ + FILE *fp; + int rc, err; + + fp = fopen(device, "w"); + if (!fp) { + if (log) { + err = errno; + syslog(LOG_ERR, "failed to open device %s for write : %s", + device, strerror(err)); + errno = err; + } + return err; + } + + rc = fputs(value, fp); + fclose(fp); + + if (rc < 0) { + if (log) { + err = errno; + syslog(LOG_ERR, "failed to write to device %s", device); + errno = err; + } + return -1; + } + + return 0; +} + +static int write_sysfs_raw(const char *sysfs_path, char* buffer) +{ + return write_sysfs_raw_internal(sysfs_path, buffer, 1); +} + +// Returns 0 for success, or -1 on failures. +static int write_sysfs_int(const char *sysfs_path, int buffer) +{ + int rc; + char writeBuf[PATH_CACHE_SIZE]; + + if (sysfs_path == NULL) { + errno = EINVAL; + return -1; + } + + snprintf(writeBuf, PATH_CACHE_SIZE, "%d", buffer); + return write_sysfs_raw(sysfs_path, writeBuf); +} + +static int read_temp_directly_sysfs(struct sensor_info_sysfs *sensor) +{ + int ret; + int value; + char fullpath[PATH_CACHE_SIZE]; + bool use_cache = false; + int cache_str_len = 0; + + if (sensor == NULL) { + syslog(LOG_ERR, "sensor is null\n"); + return BAD_TEMP; + } + // Check if cache is available + if (sensor->path_cache != NULL) { + cache_str_len = strlen(sensor->path_cache); + if (cache_str_len != 0) + use_cache = true; + } + + if (use_cache == false) { + snprintf(fullpath, sizeof(fullpath), "%s/%s", sensor->prefix, sensor->suffix); + // Update cache, if possible. + if (sensor->path_cache != NULL) + snprintf(sensor->path_cache, (PATH_CACHE_SIZE - 1), "%s", fullpath); + use_cache = true; + } + /* + * By the time control reaches here, use_cache is always true + * or this function already returned -1. So assume the cache is always on + */ + ret = read_sysfs_int(sensor->path_cache, &value); + + /* Note that Kernel sysfs stub pre-converts raw value in xxxxx format, + * which is equivalent to xx.xxx degree - all we need to do is to divide + * the read value by 1000 + */ + if (ret < 0) + value = ret; + else + value = value / 1000; + + if (value < 0) { + syslog(LOG_ERR, "failed to read temperature bus %s", fullpath); + return BAD_TEMP; + } + + usleep(11000); + return value; +} + + +static int read_temp_sysfs(struct sensor_info_sysfs *sensor) +{ + int ret; + int value; + char fullpath[PATH_CACHE_SIZE]; + bool use_cache = false; + int cache_str_len = 0; + + if (sensor == NULL) { + syslog(LOG_ERR, "sensor is null\n"); + return BAD_TEMP; + } + // Check if cache is available + if (sensor->path_cache != NULL) { + cache_str_len = strlen(sensor->path_cache); + if (cache_str_len != 0) + use_cache = true; + } + + if (use_cache == false) { + // No cached value yet. Calculate the full path first + ret = assemble_sysfs_path(sensor->prefix, sensor->suffix, fullpath, sizeof(fullpath)); + if (ret != 0) { + syslog(LOG_ERR, "%s: I2C bus %s not available. Failed reading %s\n", __FUNCTION__, sensor->prefix, sensor->suffix); + return BAD_TEMP; + } + // Update cache, if possible. + if (sensor->path_cache != NULL) + snprintf(sensor->path_cache, (PATH_CACHE_SIZE - 1), "%s", fullpath); + use_cache = true; + } + + /* + * By the time control reaches here, use_cache is always true + * or this function already returned -1. So assume the cache is always on + */ + ret = read_sysfs_int(sensor->path_cache, &value); + + /* Note that Kernel sysfs stub pre-converts raw value in xxxxx format, + * which is equivalent to xx.xxx degree - all we need to do is to divide + * the read value by 1000 + */ + if (ret < 0) + value = ret; + else + value = value / 1000; + + if (value < 0) { + syslog(LOG_ERR, "failed to read temperature bus %s", fullpath); + return BAD_TEMP; + } + + usleep(11000); + return value; +} + + +static int read_critical_max_temp(void) +{ + int i; + int temp, max_temp = BAD_TEMP; +#ifdef FANCTRL_SIMULATION + static float t = 50; + static float div = -0.25; +#endif + + struct board_info_stu_sysfs *info; + + for (i = 0; i < BOARD_INFO_SIZE; i++) { + info = &board_info[i]; + if (info->slot_id != direction) + continue; + if (info->critical && (info->flag & CRITICAL_SENSOR_BIT)) { + temp = info->critical->read_sysfs(info->critical); + if (temp != BAD_TEMP) { + if (info->critical->error_cnt) + syslog(LOG_WARNING, "%s is NORMAL", info->name); + info->critical->error_cnt = 0; + temp += info->correction; + if ((info->critical->t2 == BAD_TEMP) || (info->critical->t2 == 0)) + info->critical->t2 = temp; + else + info->critical->t2 = info->critical->t1; + if ((info->critical->t1 == BAD_TEMP) || (info->critical->t1 == 0)) + info->critical->t1 = temp; + else + info->critical->t1 = info->critical->temp; + info->critical->temp = temp; + } else { + if (info->critical->error_cnt < ERROR_TEMP_MAX) + info->critical->error_cnt++; + if (info->critical->error_cnt == 1) + syslog(LOG_WARNING, "Sensor [%s] temp lost detected", info->name); + } + if (info->critical->temp > max_temp) + max_temp = info->critical->temp; + } + } +#ifdef FANCTRL_SIMULATION + if (t <= 15) { + div = 0.25; + } else if (t >= 50) { + div = -0.25; + } + t += div; + max_temp = (int)t; +#endif +#ifdef DEBUG + syslog(LOG_DEBUG, "[zmzhan]%s: critical: max_temp=%d", __func__, max_temp); +#endif + return max_temp; +} + +static int calculate_line_pwm(void) +{ + int max_pwm = 0; + int pwm = 0; + struct board_info_stu_sysfs *info; + int i; + for (i = 0; i < BOARD_INFO_SIZE; i++) { + info = &board_info[i]; + if (info->slot_id != direction) + continue; + if (info->critical && (info->flag & CRITICAL_SENSOR_BIT)) { + if (info->critical->error_cnt) { + if (info->critical->error_cnt == ERROR_TEMP_MAX) { + if (policy->old_pwm != FAN_MAX) + syslog(LOG_ERR, "%s status is ABNORMAL, get %s failed", info->name, info->name); + pwm = FAN_MAX; + } + else { + pwm = policy->old_pwm; + } + } else { + pwm = policy->line->get_speed(info->critical, policy->line); + } + if (max_pwm < pwm) + max_pwm = pwm; + } + } + + return max_pwm; +} + +static int read_pid_max_temp(void) +{ + int i; + int temp, max_temp = BAD_TEMP; + struct board_info_stu_sysfs *info; + + for (i = 0; i < BOARD_INFO_SIZE; i++) { + info = &board_info[i]; + if (info->slot_id != direction) + continue; + if (info->critical && (info->flag & PID_CTRL_BIT)) { + temp = info->critical->read_sysfs(info->critical); + if (temp != BAD_TEMP) { + if (info->critical->error_cnt) + syslog(LOG_WARNING, "%s is NORMAL", info->name); + info->critical->error_cnt = 0; + temp += info->correction; + if ((info->critical->t2 == BAD_TEMP) || (info->critical->t2 == 0)) + info->critical->t2 = temp; + else + info->critical->t2 = info->critical->t1; + if ((info->critical->t1 == BAD_TEMP) || (info->critical->t1 == 0)) + info->critical->t1 = temp; + else + info->critical->t1 = info->critical->temp; + info->critical->temp = temp; + } else { + if (info->critical->error_cnt < ERROR_TEMP_MAX) { + info->critical->error_cnt++; + if (info->critical->error_cnt == 1) + syslog(LOG_WARNING, "Sensor [%s] temp lost detected", info->name); + } + } + if (info->critical->temp > max_temp) + max_temp = info->critical->temp; +#ifdef DEBUG + syslog(LOG_DEBUG, "[zmzhan]%s: %s: temp=%d", __func__, info->name, temp); +#endif + } + } + + return max_temp; +} + +static int calculate_pid_pwm(int fan_pwm) +{ + int i; + int pwm, max_pwm = 0; + struct board_info_stu_sysfs *info; + struct sensor_info_sysfs *critical; + + for (i = 0; i < BOARD_INFO_SIZE; i++) { + info = &board_info[i]; + if (info->slot_id != direction) + continue; + if (info->critical && (info->flag & PID_CTRL_BIT)) { + critical = info->critical; + critical->old_pwm = fan_pwm; + + if (critical->error_cnt) { + if (critical->error_cnt == ERROR_TEMP_MAX) { + if (critical->old_pwm != FAN_MAX) + syslog(LOG_ERR, "%s status is ABNORMAL, get %s failed", info->name, info->name); + pwm = FAN_MAX; + } + else { + pwm = critical->old_pwm; + } + } else { + pwm = critical->old_pwm + critical->p * (critical->temp - critical->t1) + + critical->i * (critical->temp - critical->setpoint) + + critical->d * (critical->temp + critical->t2 - 2 * critical->t1); + } +#ifdef DEBUG + syslog(LOG_DEBUG, "[zmzhan]%s: %s: pwm=%d, old_pwm=%d, p=%f, i=%f, d=%f, setpoint=%f \ + temp=%d, t1=%d, t2=%d", __func__, info->name, pwm, critical->old_pwm, critical->p, + critical->i, critical->d, critical->setpoint, critical->temp, critical->t1, critical->t2); +#endif + if (pwm < critical->min_output) + pwm = critical->min_output; + if (pwm > max_pwm) + max_pwm = pwm; + if (max_pwm > critical->max_output) + max_pwm = critical->max_output; +#ifdef DEBUG + syslog(LOG_DEBUG, "[zmzhan]%s: %s: pwm=%d, old_pwm=%d, p=%f, i=%f, d=%f, setpoint=%f \ + temp=%d, t1=%d, t2=%d", __func__, info->name, pwm, critical->old_pwm, critical->p, + critical->i, critical->d, critical->setpoint, critical->temp, critical->t1, critical->t2); +#endif + } + } + + return max_pwm; +} + + +static int alarm_temp_update(int *alarm) +{ + int i, fan; + int temp, max_temp = 0; + struct board_info_stu_sysfs *info; + *alarm &= ~HIGH_MAX_BIT; + + for (i = 0; i < BOARD_INFO_SIZE; i++) { + info = &board_info[i]; + if ((info->slot_id != direction) || (info->flag == DISABLE)) + continue; + if (info->alarm) { + temp = info->alarm->temp; + if (info->hwarn != BAD_TEMP && + (temp >= info->hwarn || ((info->flag & HIGH_MAX_BIT) && + (info->hwarn - temp <= ALARM_TEMP_THRESHOLD) && info->warn_count))) { + if (++info->warn_count >= ALARM_START_REPORT) { + if (!(info->flag & HIGH_WARN_BIT)) + syslog(LOG_ERR, "%s exceeded upper critical, value is %d C, upper critical is %d C", + info->name, temp, info->hwarn); + for (fan = 0; fan < TOTAL_FANS; fan++) { + write_fan_speed(fan, FAN_MAX); + } + write_psu_fan_speed(fan, FAN_MAX); + info->warn_count = 0; + info->flag |= (HIGH_WARN_BIT | HIGH_MAX_BIT); + info->recovery_count = 0; + } + } else if (info->lwarn != BAD_TEMP && + (temp >= info->lwarn || ((info->flag & LOW_WARN_BIT) && + (info->lwarn - temp <= ALARM_TEMP_THRESHOLD) && info->warn_count))) { + if (++info->warn_count >= ALARM_START_REPORT) { + if (!(info->flag & LOW_WARN_BIT)) + syslog(LOG_WARNING, "%s exceeded upper high, value is %d C, upper high is %d C", + info->name, temp, info->lwarn); + info->warn_count = 0; + info->flag |= LOW_WARN_BIT; + info->recovery_count = 0; + } + } else { + if (info->flag & HIGH_WARN_BIT) { + syslog(LOG_INFO, "%s is NORMAL, value is %d C", info->name, temp); + info->flag &= ~HIGH_WARN_BIT; + } else if (info->flag & LOW_WARN_BIT) { + syslog(LOG_INFO, "%s is NORMAL, value is %d C", info->name, temp); + info->flag &= ~LOW_WARN_BIT; + } else if (info->flag & HIGH_MAX_BIT) { + info->recovery_count++; + if (info->recovery_count >= WARN_RECOVERY_COUNT) { + info->flag &= ~HIGH_MAX_BIT; + syslog(LOG_INFO, "%s is NORMAL, set fan normal speed", info->name); + } +#ifdef DEBU + syslog(LOG_DEBUG, "[xuth] Major max bit: %d, recovery count: %d", *alarm & HIGH_MAX_BIT ? 1 : 0, info->recovery_count); +#endif + } + } + + if (info->flag & HIGH_MAX_BIT) + *alarm |= HIGH_MAX_BIT; + } + } + if (*alarm & HIGH_MAX_BIT) + fan_speed_temp = FAN_MAX; + + return max_temp; +} + +static inline int check_fan_normal_pwm(int pwm) +{ + if (pwm > FAN_NORMAL_MAX) + return FAN_NORMAL_MAX; + if (pwm < FAN_NORMAL_MIN) + return FAN_NORMAL_MIN; + return pwm; +} + +static inline int check_fan_one_fail_pwm(int pwm) +{ + if (pwm > FAN_ONE_FAIL_MAX) + return FAN_ONE_FAIL_MAX; + if (pwm < FAN_ONE_FAIL_MIN) + return FAN_ONE_FAIL_MIN; + return pwm; +} + +static inline int check_fan_speed(int speed, struct line_policy *line) +{ + if (speed > line->end.speed) + return line->end.speed; + if (speed < line->begin.speed) + return line->begin.speed; + return speed; +} + +static inline float get_line_k(struct point begin, struct point end) +{ + return (float)(end.speed - begin.speed) / (end.temp - begin.temp); +} + +static inline int check_fall_temp(int temp, struct line_policy *line) +{ + if (temp > line->end.temp) + return line->end.temp; + if (temp < line->begin.temp) + return line->begin.temp; + return temp; +} + +static inline int get_fall_temp(int speed, struct line_policy *line) +{ + float k = get_line_k(line->begin, line->end); + int fall_temp = (speed + 1 - line->begin.speed) / k + line->begin.temp; + return check_fall_temp(fall_temp, line); +} + +static int calculate_line_speed(struct sensor_info_sysfs *sensor, struct line_policy *line) +{ + float k = get_line_k(line->begin, line->end); + int fall_temp = get_fall_temp(policy->old_pwm, line); + int speed; + int cur_temp = sensor->temp; + int old_temp = sensor->t1; + + if (cur_temp > old_temp) { + speed = (int)(k * (cur_temp - line->begin.temp) + line->begin.speed); +#ifdef DEBUG + syslog(LOG_DEBUG, "[xuth]%s: cur_temp=%d cal_last_temp=%d k=%f Raising line_pwm=%d", + __func__, cur_temp, fall_temp, k, speed); +#endif + } else { + if (fall_temp - cur_temp <= line->temp_hyst) { + speed = (int)(k * (fall_temp - line->begin.temp) + line->begin.speed); + } else { + speed = (int)(k * (cur_temp - line->begin.temp) + line->begin.speed); + } +#ifdef DEBUG + syslog(LOG_DEBUG, "[xuth]%s: cur_temp=%d cal_last_temp=%d k=%f Falling line_pwm=%d", + __func__, cur_temp, fall_temp, k, speed); +#endif + } + + return check_fan_speed(speed, line); +} + +int calculate_pid_speed(struct pid_policy *pid) +{ + int output = 0; + output = pid->last_output + pid->kp * (pid->cur_temp - pid->t1) + + pid->ki * (pid->cur_temp - pid->set_point) + + pid->kd * (pid->cur_temp + pid->t2 - 2 * pid->t1); + if (output > pid->max_output) { + return pid->max_output; + } + if (output < pid->min_output) { + return pid->min_output; + } + return output; +} + +static int calculate_fan_normal_pwm(int cur_temp, int last_temp) +{ + int value; + int fall_temp = (policy->old_pwm + 1 - FAN_NORMAL_MIN) / NORMAL_K + RAISING_TEMP_LOW; + if (fall_temp > RAISING_TEMP_HIGH) fall_temp = RAISING_TEMP_HIGH; + if (fall_temp < RAISING_TEMP_LOW) fall_temp = RAISING_TEMP_LOW; + + if (cur_temp >= fall_temp) { + value = (int)(NORMAL_K * (cur_temp - RAISING_TEMP_LOW) + FAN_NORMAL_MIN); +#ifdef DEBUG + syslog(LOG_DEBUG, "[xuth]%s: cur_temp=%d last_temp=%d cal_last_temp=%d k=%f Raising line_pwm=%d", + __func__, cur_temp, last_temp, fall_temp, NORMAL_K, value); +#endif + } else { + if (fall_temp - cur_temp <= CRITICAL_TEMP_HYST) { + value = (int)(NORMAL_K * (fall_temp - RAISING_TEMP_LOW) + FAN_NORMAL_MIN); + } + else { + value = (int)(NORMAL_K * (cur_temp - RAISING_TEMP_LOW) + FAN_NORMAL_MIN); + } +#ifdef DEBUG + syslog(LOG_DEBUG, "[xuth]%s: cur_temp=%d last_temp=%d cal_last_temp=%d k=%f Falling line_pwm=%d", + __func__, cur_temp, last_temp, fall_temp, NORMAL_K, value); +#endif + } + + return check_fan_normal_pwm(value); +} + +static int calculate_fan_one_fail_pwm(int cur_temp, int last_temp) +{ + int value; + int fall_temp = (policy->old_pwm + 1 - FAN_ONE_FAIL_MIN) / ONE_FAIL_K + RAISING_TEMP_LOW; + if (fall_temp > RAISING_TEMP_HIGH) fall_temp = RAISING_TEMP_HIGH; + if (fall_temp < RAISING_TEMP_LOW) fall_temp = RAISING_TEMP_LOW; + + if (cur_temp >= fall_temp) { + value = (int)(ONE_FAIL_K * (cur_temp - RAISING_TEMP_LOW) + FAN_ONE_FAIL_MIN); +#ifdef DEBUG + syslog(LOG_DEBUG, "[xuth]%s: cur_temp=%d last_temp=%d cal_last_temp=%d k=%f One fail raising line_pwm=%d", + __func__, cur_temp, last_temp, fall_temp, ONE_FAIL_K, value); +#endif + } else { + if (fall_temp - cur_temp <= CRITICAL_TEMP_HYST) { + value = (int)(ONE_FAIL_K * (fall_temp - RAISING_TEMP_LOW) + FAN_ONE_FAIL_MIN); + } + else { + value = (int)(ONE_FAIL_K * (cur_temp - RAISING_TEMP_LOW) + FAN_ONE_FAIL_MIN); + } +#ifdef DEBUG + syslog(LOG_DEBUG, "[xuth]%s: cur_temp=%d last_temp=%d cal_last_temp=%d k=%f One fail falling line_pwm=%d", + __func__, cur_temp, last_temp, fall_temp, ONE_FAIL_K, value); +#endif + } + + return check_fan_one_fail_pwm(value); +} + +#define PSU_FAN_LOW 45 +static int calculate_psu_raising_fan_pwm(int temp) +{ + int slope; + int val; + + if (temp < RAISING_TEMP_LOW) { + return PSU_FAN_LOW; + } else if (temp >= RAISING_TEMP_LOW && temp < RAISING_TEMP_HIGH) { + slope = (FAN_HIGH - PSU_FAN_LOW) / (RAISING_TEMP_HIGH - RAISING_TEMP_LOW); + val = PSU_FAN_LOW + slope * temp; + return val; + } else { + return FAN_HIGH; + } + return FAN_HIGH; +} + +static int calculate_psu_falling_fan_pwm(int temp) +{ + int slope; + int val; + + if (temp < FALLING_TEMP_LOW) { + return PSU_FAN_LOW; + } else if (temp >= FALLING_TEMP_LOW && temp < FALLING_TEMP_HIGH) { + slope = (FAN_HIGH - PSU_FAN_LOW) / (FALLING_TEMP_HIGH - FALLING_TEMP_LOW); + val = PSU_FAN_LOW + slope * temp; + return val; + } else { + return FAN_HIGH; + } + + return FAN_HIGH; +} + +/* + * Fan number here is 0-based + * Note that 1 means present + */ +static int fan_is_present_sysfs(int fan, struct fan_info_stu_sysfs *fan_info) +{ + int ret; + char buf[PATH_CACHE_SIZE]; + int rc = 0; + struct fantray_info_stu_sysfs *fantray; + fantray = &fantray_info[fan]; + + snprintf(buf, PATH_CACHE_SIZE, "%s/%s", fan_info->prefix, fan_info->fan_present_prefix); + + rc = read_sysfs_int(buf, &ret); + if (rc < 0) { + syslog(LOG_ERR, "failed to read module present %s node", fan_info->fan_present_prefix); + return -1; + } + + usleep(11000); + + if (ret != 0) { + if (fantray->present == 1) { + syslog(LOG_ERR, "%s is ABSENT", fantray->name); + fantray->present = 0; + fantray->read_eeprom = 1; + } + } else { + if (fan < TOTAL_FANS) { + if (fantray->present == 0) { + syslog(LOG_WARNING, "%s is PRESENT", fantray->name); + fantray->present = 1; + fantray->read_eeprom = 1; + } + return 1; + } + snprintf(buf, PATH_CACHE_SIZE, "%s/%s", fan_info->prefix, fan_info->fan_status_prefix); + rc = read_sysfs_int(buf, &ret); + if (rc < 0) { + syslog(LOG_ERR, "failed to read %s status %s node", fantray->name, fan_info->fan_present_prefix); + return -1; + } + + usleep(11000); + + if (ret == 0) { + if ((fantray->present == 1) && (fantray->status == 1)) { + fantray->status = 0; + syslog(LOG_ERR, "%s is power off", fantray->name); + } + psu_led_color |= (0x1 << (fan - TOTAL_FANS)); + } else { + if ((fantray->present == 1) && (fantray->status == 0)) { + fantray->status = 1; + fantray->read_eeprom = 1; + syslog(LOG_WARNING, "%s is power on", fantray->name); + } + if (fantray->direction != direction) + psu_led_color |= (0x1 << (fan - TOTAL_FANS)); + } + if (fantray->present == 0) { + syslog(LOG_WARNING, "%s is PRESENT", fantray->name); + fantray->present = 1; + fantray->read_eeprom = 1; + } + return 1; + } + + if (fan < TOTAL_FANS) { + sys_fan_led_color |= (0x1 << fan); + } else { + psu_led_color |= (0x1 << (fan - TOTAL_FANS)); + } + return 0; +} + + +// Note that the fan number here is 0-based +static int set_fan_sysfs(int fan, int value) +{ + int ret; + struct fantray_info_stu_sysfs *fantray; + struct fan_info_stu_sysfs *fan_info; + + fantray = &fantray_info[fan]; + fan_info = &fantray->fan1; + + char fullpath[PATH_CACHE_SIZE]; + + ret = fan_is_present_sysfs(fan, fan_info); + if (ret == 0) { + fantray->present = 0; //not present + sys_fan_led_color |= (0x1 << fan); + return -1; + } else if (ret == 1) { + fantray->present = 1; + } else { + sys_fan_led_color |= (0x1 << fan); + return -1; + } + + if (fantray->direction != direction) { + if (fantray->direction != FAN_DIR_FAULT) + value = 89; + sys_fan_led_color |= (0x1 << fan); + } + snprintf(fullpath, PATH_CACHE_SIZE, "%s/%s", fan_info->prefix, fan_info->pwm_prefix); + adjust_sysnode_path(fan_info->prefix, fan_info->pwm_prefix, fullpath, sizeof(fullpath)); + ret = write_sysfs_int(fullpath, value); + if (ret < 0) { + syslog(LOG_ERR, "failed to set fan %s/%s, value %#x", + fan_info->prefix, fan_info->pwm_prefix, value); + return -1; + } + usleep(11000); + + return 0; +} + +static int write_fan_led_sysfs(int fan, const int color) +{ + int ret; + char fullpath[PATH_CACHE_SIZE]; + struct fantray_info_stu_sysfs *fantray; + struct fan_info_stu_sysfs *fan_info; + + fantray = &fantray_info[fan]; + fan_info = &fantray->fan1; + + + ret = fan_is_present_sysfs(fan, fan_info); + if (ret == 0) { + fantray->present = 0; //not present + return -1; + } else if (ret == 1) { + fantray->present = 1; + } else { + return -1; + } + + snprintf(fullpath, PATH_CACHE_SIZE, "%s/%s", fan_info->prefix, fan_info->fan_led_prefix); + ret = write_sysfs_int(fullpath, color); + if (ret < 0) { + syslog(LOG_ERR, "failed to set fan %s/%s, value %#x", + fan_info->prefix, fan_info->fan_led_prefix, color); + return -1; + } + usleep(11000); + + return 0; +} + + +/* Set fan speed as a percentage */ +static int write_fan_speed(const int fan, const int value) +{ + return set_fan_sysfs(fan, value); +} + +/* Set up fan LEDs */ +static int write_fan_led(const int fan, const int color) +{ + return write_fan_led_sysfs(fan, color); +} + +static int write_sys_fan_led(const int color) +{ + int ret; + char fullpath[PATH_CACHE_SIZE]; + snprintf(fullpath, PATH_CACHE_SIZE, SYS_FAN_LED_PATH); + ret = write_sysfs_int(fullpath, color); + if (ret < 0) { + syslog(LOG_ERR, "failed to set fan %s, value %#x", SYS_FAN_LED_PATH, color); + return -1; + } + usleep(11000); + + return 0; +} + +static int get_psu_pwm(void) +{ + int i; + int ret; + int pwm = 0, tmp = 0; + struct fantray_info_stu_sysfs *fantray; + struct fan_info_stu_sysfs *fan_info; + + for (i = TOTAL_FANS; i < TOTAL_FANS + TOTAL_PSUS; i++) { + fantray = &fantray_info[i]; + fan_info = &fantray->fan1; + + char fullpath[PATH_CACHE_SIZE]; + + ret = fan_is_present_sysfs(i, fan_info); + if (ret == 0) { + fantray->present = 0; //not present + continue; + } else if (ret == 1) { + fantray->present = 1; + if (fantray->status == 0) + continue; + } else { + continue; + } + + snprintf(fullpath, PATH_CACHE_SIZE, "%s/%s", fan_info->rear_fan_prefix, fan_info->pwm_prefix); + adjust_sysnode_path(fan_info->rear_fan_prefix, fan_info->pwm_prefix, fullpath, sizeof(fullpath)); + read_sysfs_int(fullpath, &tmp); + if (tmp > 100) + tmp = 100; + if (tmp < 0) + tmp = 0; + if (tmp > pwm) + pwm = tmp; + usleep(11000); + } + + pwm = pwm * FAN_MAX / 100; + + return pwm; +} +/* Set PSU fan speed as a percentage */ +static int write_psu_fan_speed(const int fan, int value) +{ + int i; + int ret; + char fullpath[PATH_CACHE_SIZE]; + struct fantray_info_stu_sysfs *fantray; + struct fan_info_stu_sysfs *fan_info; + + + value = value * 100 / FAN_MAX; //convert it to pct + for (i = TOTAL_FANS; i < TOTAL_FANS + TOTAL_PSUS; i++) { + fantray = &fantray_info[i]; + fan_info = &fantray->fan1; + + ret = fan_is_present_sysfs(i, fan_info); + if (ret == 0) { + fantray->present = 0; //not present + continue; + } else if (ret == 1) { + fantray->present = 1; + if (fantray->status == 0) //power off + continue; + } else { + continue; + } + snprintf(fullpath, PATH_CACHE_SIZE, "%s/%s", fan_info->rear_fan_prefix, PSU_SPEED_CTRL_NODE); + adjust_sysnode_path(fan_info->rear_fan_prefix, PSU_SPEED_CTRL_NODE, fullpath, sizeof(fullpath)); + ret = write_sysfs_int(fullpath, PSU_SPEED_CTRL_ENABLE); + if (ret < 0) { + syslog(LOG_ERR, "failed to enable control PSU speed"); + } + + snprintf(fullpath, PATH_CACHE_SIZE, "%s/%s", fan_info->rear_fan_prefix, fan_info->pwm_prefix); + adjust_sysnode_path(fan_info->rear_fan_prefix, fan_info->pwm_prefix, fullpath, sizeof(fullpath)); + if (fantray->direction == direction) { + ret = write_sysfs_int(fullpath, value); + } else { + ret = write_sysfs_int(fullpath, 35); + } + if (ret < 0) { + syslog(LOG_ERR, "failed to set fan %s/%s, value %#x", + fan_info->prefix, fan_info->pwm_prefix, value); + continue; + } + usleep(11000); + } + + return 0; +} + +/* Set up fan LEDs */ +static int write_psu_fan_led(const int fan, const int color) +{ + int err; + + err = write_fan_led_sysfs(fan, color); + + return err; +} + +static int fan_rpm_to_pct(const struct rpm_to_pct_map *table, const int table_len, int rpm) +{ + int i; + + for (i = 0; i < table_len; i++) { + if (table[i].rpm > rpm) { + break; + } + } + + /* + * If the fan RPM is lower than the lowest value in the table, + * we may have a problem -- fans can only go so slow, and it might + * have stopped. In this case, we'll return an interpolated + * percentage, as just returning zero is even more problematic. + */ + + if (i == 0) { + return (rpm * table[i].pct) / table[i].rpm; + } else if (i == table_len) { // Fell off the top? + return table[i - 1].pct; + } + + // Interpolate the right percentage value: + + int percent_diff = table[i].pct - table[i - 1].pct; + int rpm_diff = table[i].rpm - table[i - 1].rpm; + int fan_diff = table[i].rpm - rpm; + + return table[i].pct - (fan_diff * percent_diff / rpm_diff); +} + +/*return: 1 OK, 0 not OK*/ +int fan_speed_okay(const int fan, int speed, const int slop) +{ + int ret; + char buf[PATH_CACHE_SIZE]; + int rc = 1; + int front_speed, front_pct; + int rear_speed, rear_pct; + struct fantray_info_stu_sysfs *fantray; + struct fan_info_stu_sysfs *fan_info; + + fantray = &fantray_info[fan]; + fan_info = &fantray->fan1; + + ret = fan_is_present_sysfs(fan, fan_info); + if (ret == 0) { + fantray->present = 0; //not present + return 0; + } else if (ret == 1) { + fantray->present = 1; + } else { + return 0; + } + if (fantray->direction != direction) + return 0; + snprintf(buf, PATH_CACHE_SIZE, "%s/%s", fan_info->prefix, fan_info->front_fan_prefix); + + rc = read_sysfs_int(buf, &ret); + if (rc < 0) { + syslog(LOG_ERR, "failed to read %s node", fan_info->front_fan_prefix); + return -1; + } + front_speed = ret; + usleep(11000); + if (front_speed < FAN_FAIL_RPM) { + fan_info->front_failed++; + if (fan_info->front_failed == 1) + syslog(LOG_WARNING, "%s-1 speed %d, less than %d detected", + fantray->name, front_speed, FAN_FAIL_RPM); + if (fan_info->front_failed == FAN_FAIL_COUNT) + syslog(LOG_ERR, "%s-1 status is ABNORMAL, speed less than 1000 RPM for over 30 seconds", + fantray->name); + if (fan_info->front_failed > FAN_FAIL_COUNT) + fan_info->front_failed = FAN_FAIL_COUNT; + } else if (speed == FAN_MAX && (front_speed < (FAN_FRONTT_SPEED_MAX * (100 - slop) / 100))) { + fan_info->front_failed++; + if (fan_info->front_failed == 1) + syslog(LOG_WARNING, "%s-1 speed %d, less than %d%% of max speed(%d) detected", + fantray->name, front_speed, 100 - slop, speed); + if (fan_info->front_failed == FAN_FAIL_COUNT) + syslog(LOG_ERR, "%s-1 status is ABNORMAL, speed is set to 100%% but real speed is lower than 70%% of max speed", + fantray->name); + if (fan_info->front_failed > FAN_FAIL_COUNT) + fan_info->front_failed = FAN_FAIL_COUNT; + } else { + if (fan_info->front_failed) + syslog(LOG_WARNING, "%s-1 status is NORMAL", fantray->name); + fan_info->front_failed = 0; + } + + memset(buf, 0, PATH_CACHE_SIZE); + snprintf(buf, PATH_CACHE_SIZE, "%s/%s", fan_info->prefix, fan_info->rear_fan_prefix); + + rc = read_sysfs_int(buf, &ret); + if (rc < 0) { + syslog(LOG_ERR, "failed to read %s node", fan_info->front_fan_prefix); + return -1; + } + rear_speed = ret; + if (rear_speed < FAN_FAIL_RPM) { + fan_info->rear_failed++; + if (fan_info->rear_failed == 1) + syslog(LOG_WARNING, "%s-2 speed %d, less than %d detected", + fantray->name, rear_speed, FAN_FAIL_RPM); + if (fan_info->rear_failed == FAN_FAIL_COUNT) + syslog(LOG_ERR, "%s-2 status is ABNORMAL, speed less than 1000 RPM for over 30 seconds", + fantray->name); + if (fan_info->rear_failed > FAN_FAIL_COUNT) + fan_info->rear_failed = FAN_FAIL_COUNT; + } else if (speed == FAN_MAX && (rear_speed < (FAN_REAR_SPEED_MAX * (100 - slop) / 100))) { + fan_info->rear_failed++; + if (fan_info->rear_failed == 1) + syslog(LOG_WARNING, "%s-2 speed %d, less than %d%% of max speed(%d) detected", + fantray->name, rear_speed, 100 - slop, speed); + if (fan_info->rear_failed == FAN_FAIL_COUNT) + syslog(LOG_ERR, "%s-2 status is ABNORMAL, speed is set to 100%% but real speed is lower than 70%% of max speed", + fantray->name); + if (fan_info->rear_failed > FAN_FAIL_COUNT) + fan_info->rear_failed = FAN_FAIL_COUNT; + } else { + if (fan_info->rear_failed) + syslog(LOG_WARNING, "%s-2 status is NORMAL", fantray->name); + fan_info->rear_failed = 0; + } + + if (fan_info->front_failed >= FAN_FAIL_COUNT && fan_info->rear_failed >= FAN_FAIL_COUNT) { + if (fantray->failed == 0) + syslog(LOG_WARNING, "%s failed, set fan max speed", fantray->name); + fantray->failed = 1; + } + + if (fan_info->front_failed >= FAN_FAIL_COUNT || fan_info->rear_failed >= FAN_FAIL_COUNT) { + return 0; + } + + return 1; + +} + +/*return: 1 OK, 0 not OK*/ +int psu_speed_okay(const int fan, int speed, const int slop) +{ + int ret; + char buf[PATH_CACHE_SIZE]; + int rc = 0; + int psu_speed, pct; + struct fantray_info_stu_sysfs *fantray; + struct fan_info_stu_sysfs *fan_info; + + fantray = &fantray_info[fan]; + fan_info = &fantray->fan1; + + ret = fan_is_present_sysfs(fan, fan_info); + if (ret == 0) { + fantray->present = 0; //not present + return 0; + } else if (ret == 1) { + fantray->present = 1; + if (fantray->status == 0) + return 0; + } else { + return 0; + } + + snprintf(buf, PATH_CACHE_SIZE, "%s/%s", fan_info->rear_fan_prefix, fan_info->front_fan_prefix); + adjust_sysnode_path(fan_info->rear_fan_prefix, fan_info->front_fan_prefix, buf, sizeof(buf)); + rc = read_sysfs_int(buf, &ret); + if (rc < 0) { + syslog(LOG_ERR, "failed to read %s node", fan_info->front_fan_prefix); + return -1; + } + psu_speed = ret; + usleep(11000); + if (psu_speed < FAN_FAIL_RPM) { + fan_info->front_failed++; + if (fan_info->front_failed == 1) + syslog(LOG_WARNING, "%s speed %d, less than %d detected", + fantray->name, psu_speed, FAN_FAIL_RPM); + if (fan_info->front_failed == FAN_FAIL_COUNT) + syslog(LOG_ERR, "%s status is ABNORMAL, speed less than 1000 RPM for over 30 seconds", + fantray->name); + if (fan_info->front_failed > FAN_FAIL_COUNT) + fan_info->front_failed = FAN_FAIL_COUNT; + } else if (speed == FAN_MAX && (psu_speed < (PSU_SPEED_MAX * (100 - slop) / 100))) { + fan_info->front_failed++; + if (fan_info->front_failed == 1) + syslog(LOG_WARNING, "%s speed %d, less than %d%% of max speed(%d) detected", + fantray->name, psu_speed, 100 - slop, speed); + if (fan_info->front_failed == FAN_FAIL_COUNT) + syslog(LOG_ERR, "%s status is ABNORMAL, speed is set to 100%% but real speed is lower than 70%% of max speed", + fantray->name); + if (fan_info->front_failed > FAN_FAIL_COUNT) + fan_info->front_failed = FAN_FAIL_COUNT; + } else { + if (fan_info->front_failed) + syslog(LOG_WARNING, "%s status is NORMAL", fantray->name); + fan_info->front_failed = 0; + } + + if (fan_info->front_failed >= FAN_FAIL_COUNT) + return 0; + + return 1; + +} + +static int fancpld_watchdog_enable(void) +{ + int ret; + char fullpath[PATH_CACHE_SIZE]; + + snprintf(fullpath, PATH_CACHE_SIZE, "%s", FAN_WDT_ENABLE_SYSFS); + ret = write_sysfs_int(fullpath, 1); + if (ret < 0) { + syslog(LOG_ERR, "failed to set fan %s, value 1", + FAN_WDT_ENABLE_SYSFS); + return -1; + } + usleep(11000); + return 0; +} + +static int system_shutdown(const char *why) +{ + int ret; + + syslog(LOG_EMERG, "Shutting down: %s", why); + + ret = write_sysfs_int(PSU1_SHUTDOWN_SYSFS, 1); + if (ret < 0) { + syslog(LOG_ERR, "failed to set PSU1 shutdown"); + return -1; + } + ret = write_sysfs_int(PSU2_SHUTDOWN_SYSFS, 1); + if (ret < 0) { + syslog(LOG_ERR, "failed to set PSU2 shutdown"); + return -1; + } + + sleep(2); + exit(2); + + return 0; +} + +char* find_sub_string(char *src, const char *sub, int src_len) +{ + for (int i = 0; i < src_len; i++) { + if (*(src + i) == *sub) { + int flag = 1; + for (int index = 1; index < strlen(sub); index++) { + if (*(src + index + i) != *(sub + index)) { + flag = 0; + break; + } + } + if (flag) return (src + i); + } + } + return NULL; +} + +static int get_fan_direction(int direction) +{ + struct fantray_info_stu_sysfs *fantray; + char buffer[128]; + char command[128]; + int f2r_fan_cnt = 0; + int r2f_fan_cnt = 0; + FILE *fp; + int i = 0; + char *pn; + int ret; + + for (; i < TOTAL_FANS + TOTAL_PSUS; i++) + { + if (i >= sizeof(fan_eeprom_path) / sizeof(fan_eeprom_path[0])) + continue; + fantray = &fantray_info[i]; + if (!fantray->read_eeprom) + continue; + fp = fopen(fan_eeprom_path[i], "rb"); + if (!fp) { + if (fantray->direction != FAN_DIR_FAULT) { + syslog(LOG_ERR, "failed to get %s direction", fantray->name); + syslog(LOG_WARNING, "%s direction changed to [Fault]", fantray->name); + fantray->direction = FAN_DIR_FAULT; + fantray->read_eeprom = 0; + continue; + } + } + char temp; + int len; + memset(buffer, 0, sizeof(buffer)); + ret = fread(buffer, sizeof(char), sizeof(buffer), fp); + fclose(fp); + if (i < TOTAL_FANS) { + if (pn = find_sub_string(buffer, FAN_DIR_F2B_STR, sizeof(buffer))) { + f2r_fan_cnt++; + if (fantray->direction == FAN_DIR_FAULT) { + syslog(LOG_WARNING, "%s eeprom is NORMAL", fantray->name); + if (fantray->eeprom_fail) { + syslog(LOG_WARNING, "%s model match, part number is %s", fantray->name, FAN_DIR_F2B_STR); + fantray->eeprom_fail = 0; + } + } + fantray->direction = FAN_DIR_F2B; + if (direction != fantray->direction) + syslog(LOG_ERR, "%s airflow direction mismatch, direction is F2B, system direction is B2F", fantray->name); + else + syslog(LOG_WARNING, "%s airflow direction match, direction is F2B, system direction is F2B", fantray->name); + } else if (find_sub_string(buffer, FAN_DIR_B2F_STR, sizeof(buffer))) { + r2f_fan_cnt++; + if (fantray->direction == FAN_DIR_FAULT) { + syslog(LOG_WARNING, "%s eeprom is NORMAL", fantray->name); + if (fantray->eeprom_fail) { + syslog(LOG_WARNING, "%s model match, part number is %s", fantray->name, FAN_DIR_B2F_STR); + fantray->eeprom_fail = 0; + } + } + fantray->direction = FAN_DIR_B2F; + if (direction != fantray->direction) + syslog(LOG_ERR, "%s airflow direction mismatch, direction is B2F, system direction is F2B", fantray->name); + else + syslog(LOG_WARNING, "%s airflow direction match, direction is B2F, system direction is B2F", fantray->name); + } else { + fantray->direction = FAN_DIR_FAULT; + if (ret > 0) { + fantray->eeprom_fail = 1; + syslog(LOG_CRIT, "%s model mismatch, part number is %s", fantray->name, pn); + } else { + syslog(LOG_WARNING, "%s eeprom is ABNORMAL, read %s eeprom failed", fantray->name, fantray->name); + } + } + } else { + if (pn = find_sub_string(buffer, DELTA_PSU_DIR_F2B_STR, sizeof(buffer))) { + if (fantray->direction == FAN_DIR_FAULT) { + syslog(LOG_WARNING, "%s eeprom is NORMAL", fantray->name); + if (fantray->eeprom_fail) { + syslog(LOG_WARNING, "%s model match, part number is %s", fantray->name, DELTA_PSU_DIR_F2B_STR); + fantray->eeprom_fail = 0; + } + } + fantray->direction = FAN_DIR_F2B; + if (direction != fantray->direction) + syslog(LOG_ERR, "%s airflow direction mismatch, direction is F2B, system direction is B2F", fantray->name); + else + syslog(LOG_WARNING, "%s airflow direction match, direction is F2B, system direction is F2B", fantray->name); + } else if (find_sub_string(buffer, DELTA_PSU_DIR_B2F_STR, sizeof(buffer))) { + if (fantray->direction == FAN_DIR_FAULT) { + syslog(LOG_WARNING, "%s eeprom is NORMAL", fantray->name); + if (fantray->eeprom_fail) { + syslog(LOG_WARNING, "%s model match, part number is %s", fantray->name, DELTA_PSU_DIR_B2F_STR); + fantray->eeprom_fail = 0; + } + } + fantray->direction = FAN_DIR_B2F; + if (direction != fantray->direction) + syslog(LOG_ERR, "%s airflow direction mismatch, direction is B2F, system direction is F2B", fantray->name); + else + syslog(LOG_WARNING, "%s airflow direction match, direction is B2F, system direction is B2F", fantray->name); + } else if (find_sub_string(buffer, ACBEL_PSU_DIR_F2B_STR, sizeof(buffer))) { + if (fantray->direction == FAN_DIR_FAULT) { + syslog(LOG_WARNING, "%s eeprom is NORMAL", fantray->name); + if (fantray->eeprom_fail) { + syslog(LOG_WARNING, "%s model match, part number is %s", fantray->name, ACBEL_PSU_DIR_F2B_STR); + fantray->eeprom_fail = 0; + } + } + fantray->direction = FAN_DIR_F2B; + if (direction != fantray->direction) + syslog(LOG_ERR, "%s airflow direction mismatch, direction is F2B, system direction is B2F", fantray->name); + else + syslog(LOG_WARNING, "%s airflow direction match, direction is F2B, system direction is F2B", fantray->name); + } else if (find_sub_string(buffer, ACBEL_PSU_DIR_B2F_STR, sizeof(buffer))) { + if (fantray->direction == FAN_DIR_FAULT) { + syslog(LOG_WARNING, "%s eeprom is NORMAL", fantray->name); + if (fantray->eeprom_fail) { + syslog(LOG_WARNING, "%s model match, part number is %s", fantray->name, ACBEL_PSU_DIR_B2F_STR); + fantray->eeprom_fail = 0; + } + } + fantray->direction = FAN_DIR_B2F; + if (direction != fantray->direction) + syslog(LOG_ERR, "%s airflow direction mismatch, direction is B2F, system direction is F2B", fantray->name); + else + syslog(LOG_WARNING, "%s airflow direction match, direction is B2F, system direction is B2F", fantray->name); + } else { + fantray->direction = FAN_DIR_FAULT; + if (ret > 0) { + fantray->eeprom_fail = 1; + syslog(LOG_CRIT, "%s model mismatch, part number is %s", fantray->name, pn); + } else { + syslog(LOG_WARNING, "%s eeprom is ABNORMAL, read %s eeprom failed", fantray->name, fantray->name); + } + } + } + fantray->read_eeprom = 0; + } + + if (f2r_fan_cnt >= r2f_fan_cnt) { + return FAN_DIR_F2B; + } else { + return FAN_DIR_B2F; + } +} + +int get_thermal_direction(void) +{ + char buffer[128]; + FILE *fp; + char command[128]; + memset(command, 0, sizeof(command)); + sprintf(command, "/usr/bin/decode-syseeprom | grep 'Part Number' 2> /dev/null"); + fp = popen(command, "r"); + int thermal_dir; + int fan, fan_speed; + + if (!fp) { + syslog(LOG_ERR, "failed to get thermal direction"); + syslog(LOG_WARNING, "thermal direction judged by fan direction"); + } else { + char temp; + int len = 0; + memset(buffer, 0, sizeof(buffer)); + fread(buffer, sizeof(char), sizeof(buffer), fp); + pclose(fp); + if (find_sub_string(buffer, THERMAL_DIR_F2B_STR, sizeof(buffer))) { + syslog(LOG_INFO, "system direction is F2B"); + thermal_dir = FAN_DIR_F2B; + } else if (find_sub_string(buffer, THERMAL_DIR_B2F_STR, sizeof(buffer))) { + syslog(LOG_INFO, "system direction is B2F"); + thermal_dir = FAN_DIR_B2F; + } else { + syslog(LOG_ERR, "system direction is unknown, FAN speed is set to 100%%"); + fan_speed = FAN_MAX; + for (fan = 0; fan < TOTAL_FANS; fan++) { + write_fan_speed(fan, fan_speed); + } + write_psu_fan_speed(fan, fan_speed); + } + } + get_fan_direction(thermal_dir); + + return thermal_dir; +} + +static void update_thermal_direction() +{ + struct fantray_info_stu_sysfs *fantray; + int dir = get_thermal_direction(); + if (direction != dir) { + direction = dir; + if (direction == FAN_DIR_F2B) { + syslog(LOG_INFO, "setting F2B thermal policy"); + policy = &f2b_normal_policy; + } + if (direction == FAN_DIR_B2F) { + syslog(LOG_INFO, "setting B2F thermal policy"); + policy = &b2f_normal_policy; + } + } +} + +static int pid_ini_parser(struct board_info_stu_sysfs *info, FILE *fp) +{ + char *p; + char buf[PID_FILE_LINE_MAX]; + struct sensor_info_sysfs *sensor; + sensor = info->critical; + + while (fgets(buf, PID_FILE_LINE_MAX, fp) != NULL) { + if (buf[0] == '#' || strlen(buf) <= 0 || buf[0] == '\r' || buf[0] == '\n') + continue; + + p = strtok(buf, "="); + while (p != NULL) { + if (!strncmp(p, "PID_enable", strlen("PID_enable"))) { + p = strtok(NULL, "="); + if (p) { + pid_using = atoi(p); + } else { + pid_using = 0; + } + if (!pid_using) + return 0; + } else if (!strncmp(p, "setpoint", strlen("setpoint"))) { + p = strtok(NULL, "="); + if (p && pid_using) + sensor->setpoint = atof(p); + } else if (!strncmp(p, "P", strlen("P"))) { + p = strtok(NULL, "="); + if (p && pid_using) + sensor->p = atof(p); + } else if (!strncmp(p, "I", strlen("I"))) { + p = strtok(NULL, "="); + if (p && pid_using) + sensor->i = atof(p); + } else if (!strncmp(p, "D", strlen("D"))) { + p = strtok(NULL, "="); + if (p && pid_using) + sensor->d = atof(p); + } else if (!strncmp(p, "min_output", strlen("min_output"))) { + p = strtok(NULL, "="); + if (p && pid_using) + sensor->min_output = atof(p); + return 0; + } else if (!strncmp(p, "max_output", strlen("max_output"))) { + p = strtok(NULL, "="); + if (p && pid_using) + sensor->max_output = atof(p); + return 0; + } + p = strtok(NULL, "="); + } + } + + return 0; +} + +static int load_pid_config(void) +{ + int i; + FILE *fp; + char buf[PID_FILE_LINE_MAX]; + char *p; + int len; + struct board_info_stu_sysfs *binfo = &board_info[0]; + + fp = fopen(PID_CONFIG_PATH, "r"); + if (!fp) { + pid_using = 0; + syslog(LOG_NOTICE, "PID configure file does not find, using default PID params"); + return 0; + } + while (fgets(buf, PID_FILE_LINE_MAX, fp) != NULL) { + len = strlen(buf); + buf[len - 1] = '\0'; + if (buf[0] == '#' || strlen(buf) <= 0 || buf[0] == '\r' || buf[0] == '\n') + continue; + p = strtok(buf, "["); + while (p != NULL) { + if (!strncmp(p, "PID enable", strlen("PID enable"))) { + pid_ini_parser(binfo, fp); + } else { + for (i = 0; i < BOARD_INFO_SIZE; i++) { + binfo = &board_info[i]; + if (!strncmp(binfo->name, p, strlen(binfo->name))) { + pid_ini_parser(binfo, fp); + break; + } + } + } + p = strtok(NULL, "["); + } + } + + fclose(fp); + return 0; +} + +static int policy_init(void) +{ + int slope; + syslog(LOG_NOTICE, "Initializing FSC policy"); + update_thermal_direction(); + + load_pid_config(); + if (pid_using == 0) { + syslog(LOG_NOTICE, "PID configure: using default PID params"); + } + + struct board_info_stu_sysfs *info; + struct sensor_info_sysfs *critical; + int i; + for (i = 0; i < BOARD_INFO_SIZE; i++) { + info = &board_info[i]; + if (info->slot_id != direction) + continue; + if (info->critical && (info->flag & PID_CTRL_BIT)) { + critical = info->critical; + syslog(LOG_INFO, "%s: setpoint=%f, p=%f, i=%f, d=%f", info->name, critical->setpoint, + critical->p, critical->i, critical->d); + } + } + + return 0; +} + +int main(int argc, char **argv) { + int critical_temp; + int old_temp = -1; + struct fantray_info_stu_sysfs *info; + int fan_speed = FAN_MEDIUM; + int bad_reads = 0; + int fan_failure = 0; + int sub_failed = 0; + int one_failed = 0; //recored one system fantray failed + int old_speed = FAN_MEDIUM; + int fan_bad[TOTAL_FANS + TOTAL_PSUS] = {0}; + int fan; + unsigned int log_count = 0; // How many times have we logged our temps? + int prev_fans_bad = 0; + int shutdown_delay = 0; + int psu_pwm; + int line_pwm = 0; + int pid_pwm = 0; + int alarm = 0; +#ifdef CONFIG_PSU_FAN_CONTROL_INDEPENDENT + int psu_old_temp = 0; + int psu_raising_pwm; + int psu_falling_pwm; + int psu_fan_speed = FAN_MEDIUM; +#endif + struct fantray_info_stu_sysfs *fantray; + struct fan_info_stu_sysfs *fan_info; + + // Initialize path cache + init_path_cache(); + + // Start writing to syslog as early as possible for diag purposes. + openlog("fand_v2", LOG_CONS, LOG_DAEMON); + daemon(1, 0); + syslog(LOG_DEBUG, "Starting up; system should have %d fans.", TOTAL_FANS); + fancpld_watchdog_enable(); + policy_init(); + sleep(5); /* Give the fans time to come up to speed */ + while (1) { + fan_speed_temp = 0; + /* Read sensors */ + critical_temp = read_critical_max_temp(); + read_pid_max_temp(); + alarm_temp_update(&alarm); + + /* + * Calculate change needed -- we should eventually + * do something more sophisticated, like PID. + * + * We should use the intake temperature to adjust this + * as well. + */ + + /* Other systems use a simpler built-in table to determine fan speed. */ + policy->old_pwm = fan_speed; + line_pwm = calculate_line_pwm(); + if (line_pwm > fan_speed_temp) + fan_speed_temp = line_pwm; +#ifdef DEBUG + syslog(LOG_DEBUG, "[zmzhan]%s: line_speed=%d", __func__, fan_speed_temp); +#endif + +#ifndef CONFIG_PSU_FAN_CONTROL_INDEPENDENT + psu_pwm = get_psu_pwm(); +#endif + if (1) { + pid_pwm = calculate_pid_pwm(fan_speed); + if (pid_pwm > fan_speed_temp) + fan_speed_temp = pid_pwm; + } + fan_speed = fan_speed_temp; +#ifdef DEBUG + syslog(LOG_DEBUG, "[zmzhan]%s: fan_speed=%d, pid_using=%d, pid_pwm=%d", + __func__, fan_speed, pid_using, pid_pwm); +#endif + policy->pwm = fan_speed; + old_temp = critical_temp; +#ifdef CONFIG_PSU_FAN_CONTROL_INDEPENDENT + psu_raising_pwm = calculate_psu_raising_fan_pwm(critical_temp); + psu_falling_pwm = calculate_psu_falling_fan_pwm(critical_temp); + if (psu_old_temp <= critical_temp) { + /*raising*/ + if (psu_raising_pwm >= psu_fan_speed) { + psu_fan_speed = psu_raising_pwm; + } + } else { + /*falling*/ + if (psu_falling_pwm <= psu_fan_speed ) { + psu_fan_speed = psu_falling_pwm; + } + } + psu_old_temp = critical_temp; +#endif + + /* + * Update fans only if there are no failed ones. If any fans failed + * earlier, all remaining fans should continue to run at max speed. + */ + if (fan_failure == 0) { + if (log_count++ % REPORT_TEMP == 0) { + syslog(LOG_NOTICE, "critical temp %d, fan speed %d%%", + critical_temp, fan_speed * 100 / FAN_MAX); + syslog(LOG_NOTICE, "Fan speed changing from %d%% to %d%%", + old_speed * 100 / FAN_MAX, fan_speed * 100 / FAN_MAX); + } + for (fan = 0; fan < TOTAL_FANS; fan++) { + write_fan_speed(fan, fan_speed); + } +#ifdef CONFIG_PSU_FAN_CONTROL_INDEPENDENT + write_psu_fan_speed(fan, psu_fan_speed); +#else + write_psu_fan_speed(fan, fan_speed); +#endif + } + + /* + * Wait for some change. Typical I2C temperature sensors + * only provide a new value every second and a half, so + * checking again more quickly than that is a waste. + * + * We also have to wait for the fan changes to take effect + * before measuring them. + */ + + sleep(3); + + /* Check fan RPMs */ + for (fan = 0; fan < TOTAL_FANS; fan++) { + /* + * Make sure that we're within some percentage + * of the requested speed. + */ + if (fan_speed_okay(fan, fan_speed, FAN_FAILURE_OFFSET)) { + if (fan_bad[fan] >= FAN_FAILURE_THRESHOLD) { + write_fan_led(fan, FAN_LED_GREEN); + syslog(LOG_CRIT, "Fan %d has recovered", fan + 1); + } + fan_bad[fan] = 0; + } else { + fan_bad[fan]++; + } + } + for (fan = TOTAL_FANS; fan < TOTAL_FANS + TOTAL_PSUS; fan++) { + if (psu_speed_okay(fan, fan_speed, FAN_FAILURE_OFFSET)) { + if (fan_bad[fan] >= FAN_FAILURE_THRESHOLD) { + syslog(LOG_CRIT, "PSU %d has recovered", fan - TOTAL_FANS + 1); + } + fan_bad[fan] = 0; + } else { + fan_bad[fan]++; + } + } + + fan_failure = 0; + sub_failed = 0; + one_failed = 0; + for (fan = 0; fan < TOTAL_FANS + TOTAL_PSUS; fan++) { + if (fan_bad[fan] >= FAN_FAILURE_THRESHOLD) { + fantray = &fantray_info[fan]; + fan_info = &fantray->fan1; + if (fan_info->front_failed >= FAN_FAIL_COUNT) { + sub_failed++; + one_failed++; +#ifdef DEBUG + syslog(LOG_DEBUG, "[zmzhan]%s:fan[%d] front_failed=%d", __func__, fan, fan_info->front_failed); +#endif + } + if (fan_info->rear_failed >= FAN_FAIL_COUNT) { + sub_failed++; + one_failed++; +#ifdef DEBUG + syslog(LOG_DEBUG, "[zmzhan]%s:fan[%d] rear_failed=%d", __func__, fan, fan_info->rear_failed); +#endif + } + if (fantray->present == 0) { + fan_failure++; + } + else if ((fantray->failed > 0) || ((fantray->direction != direction) && (fan < TOTAL_FANS))) { + fan_failure++; + } + + write_fan_led(fan, FAN_LED_RED); + } + } +#ifdef DEBUG + syslog(LOG_DEBUG, "[zmzhan]%s: fan_failure=%d, sub_failed=%d", __func__, fan_failure, sub_failed); +#endif + if (sub_failed >= 2) { + fan_failure += sub_failed / 2; + } else if (sub_failed == 1 && one_failed == 1) { + if (direction == FAN_DIR_B2F) { + policy = &b2f_one_fail_policy; + } else { + policy = &f2b_one_fail_policy; + } +#ifdef DEBUG + syslog(LOG_DEBUG, "[zmzhan]%s: Change the policy: policy=%p(fail: b2f:%p, f2b:%p)", __func__, policy, &b2f_one_fail_policy, &f2b_one_fail_policy); +#endif + } else { + if (one_failed == 0 && (policy == &b2f_one_fail_policy || policy == &f2b_one_fail_policy)) { + if (direction == FAN_DIR_B2F) { + policy = &b2f_normal_policy; + } else { + policy = &f2b_normal_policy; + } +#ifdef DEBUG + syslog(LOG_DEBUG, "[zmzhan]%s: Recovery policy: policy=%p(b2f:%p, f2b:%p)", __func__, policy, &b2f_normal_policy, &f2b_normal_policy); +#endif + } + } + if (fan_failure > 0) { + if (prev_fans_bad != fan_failure) { + syslog(LOG_CRIT, "%d fans failed", fan_failure); + } + fan_speed = FAN_MAX; + for (fan = 0; fan < TOTAL_FANS; fan++) { + write_fan_speed(fan, fan_speed); + } + write_psu_fan_speed(fan, fan_speed); + old_speed = fan_speed; + } else if (prev_fans_bad != 0 && fan_failure == 0) { + old_speed = fan_speed; + } else { + old_speed = fan_speed; + } + /* Suppress multiple warnings for similar number of fan failures. */ + prev_fans_bad = fan_failure; + if (sys_fan_led_color) + write_sys_fan_led(SYS_FAN_LED_RED); + else + write_sys_fan_led(SYS_FAN_LED_GREEN); + sys_fan_led_color = 0; + if (psu_led_color) + write_psu_fan_led(TOTAL_FANS, PSU_LED_RED); + else + write_psu_fan_led(TOTAL_FANS, PSU_LED_GREEN); + psu_led_color = 0; + usleep(11000); + get_fan_direction(direction); + } + + return 0; +} diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/fanctrl/fand_v2.h b/platform/broadcom/sonic-platform-modules-cel/tools/fanctrl/fand_v2.h new file mode 100644 index 00000000000..7abe7c1dd27 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/fanctrl/fand_v2.h @@ -0,0 +1,113 @@ +#ifndef _FAND_V2_H_ +#define _FAND_V2_H_ + +#define uchar unsigned char +#define PATH_CACHE_SIZE 256 + +struct sensor_info_sysfs { + const char *prefix; + const char *suffix; + uchar error_cnt; + int temp; + int t1; + int t2; + int old_pwm; + float setpoint; + float p; + float i; + float d; + float min_output; + float max_output; + int (*read_sysfs)(struct sensor_info_sysfs *sensor); + char path_cache[PATH_CACHE_SIZE]; +}; + +struct fan_info_stu_sysfs { + const char *prefix; + const char *front_fan_prefix; + const char *rear_fan_prefix; + const char *pwm_prefix; + const char *fan_led_prefix; + const char *fan_present_prefix; + const char *fan_status_prefix; + //uchar present; //for chassis using, other ignore it + uchar front_failed; //for single fan fail + uchar rear_failed; +}; + +struct psu_info_sysfs { + char* sysfs_path; + char* shutdown_path; + int value_to_shutdown; +}; + +struct board_info_stu_sysfs { + const char *name; + uint slot_id; + int correction; + int lwarn; + int hwarn; + int warn_count; + int recovery_count; + int flag; + struct sensor_info_sysfs *critical; + struct sensor_info_sysfs *alarm; +}; + +struct fantray_info_stu_sysfs { + const char *name; + int present; + int read_eeprom; + int status; + int failed; //for fantray fail + int direction; + int eeprom_fail; + struct fan_info_stu_sysfs fan1; +}; + +struct rpm_to_pct_map { + uint pct; + uint rpm; +}; +struct dictionary_t { + char name[20]; + int value; +}; + +struct thermal_policy { + int pwm; + int old_pwm; + struct line_policy *line; + // int (*calculate_pwm)(int cur_temp, int last_temp); +}; + +struct point { + int temp; + int speed; +}; + +struct line_policy { + int temp_hyst; + struct point begin; + struct point end; + int (*get_speed)(struct sensor_info_sysfs *sensor, struct line_policy *line); +}; + +struct pid_policy { + int cur_temp; + int t1; + int t2; + int set_point; + float kp; + float ki; + float kd; + int last_output; + int max_output; + int min_output; + int (*get_speed)(struct pid_policy pid); +}; + +static int calculate_line_speed(struct sensor_info_sysfs *sensor, struct line_policy *line); +int calculate_pid_speed(struct pid_policy *pid); + +#endif diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/fpga_prog/fpga_prog.c b/platform/broadcom/sonic-platform-modules-cel/tools/fpga_prog/fpga_prog.c new file mode 100644 index 00000000000..c640c8ad006 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/fpga_prog/fpga_prog.c @@ -0,0 +1,282 @@ +/******************************************************************** +Author: Sittisak Sinprem + flash_spi_fpga + + user-space appliction to flash SPI FLASH + + As a "root" previledge, this program can run well + while other user group would report system errors under Linux OS. + +*********************************************************************/ + +#include +#include +#include +#include //for open() +#include //for close() +#include +#include + +/** + * The FPGA SPI Flash update application + * This application read the binary image file and program + * into flash memory. The erasing time can be long the + * the WAIT_WRITE_READY_SEC should be more than 30 seconds. + */ + +#define WAIT_WRITE_READY_SEC 180 +#define WAIT_WRITE_CONTINUE_CYCLE 100000 + +#define REG_SPI_WR_EN 0x1200 +#define REG_SPI_WR_DAT 0x1204 +#define REG_SPI_CHK_ID 0x1208 +#define REG_SPI_VERIFY 0x120C +#define REG_SPI_STAT 0x1210 +#define REG_SPI_RESET 0x1214 + +#define SPI_STAT_MARK_READY (1 << 15) +#define SPI_STAT_MARK_DONE (1 << 14) +#define SPI_STAT_MARK_ERROR_ANY (1 << 13) +#define SPI_STAT_MARK_ERROR_CHKID (1 << 12) +#define SPI_STAT_MARK_ERROR_ERASE (1 << 11) +#define SPI_STAT_MARK_ERROR_PROG (1 << 10) +#define SPI_STAT_MARK_ERROR_TOUT (1 << 9) +#define SPI_STAT_MARK_ERROR_CRC (1 << 8) +#define SPI_STAT_MARK_STG_STARTED (1 << 7) +#define SPI_STAT_MARK_STG_INITED (1 << 6) +#define SPI_STAT_MARK_STG_CHECKED_ID (1 << 5) +#define SPI_STAT_MARK_STG_ERSD_SW (1 << 4) +#define SPI_STAT_MARK_STG_UP_ERSD_IMG (1 << 3) +#define SPI_STAT_MARK_STG_UP_PRG_IMG (1 << 2) +#define SPI_STAT_MARK_STG_VERIFIED (1 << 1) +#define SPI_STAT_MARK_STG_PRG_CMPT (1 << 0) + +#define debug(fmt,args...) printf("debug : "fmt"\n",##args) +#define reg_write(reg,value) func_write(reg,value) +#define reg_read(reg,value) value = func_read(reg) + +#define DEV_CHAR_FILENAME "/dev/fwupgrade" + +struct fpga_reg_data { + uint32_t reg; + uint32_t value; +}; + +enum{ + READREG, + WRITEREG +}; + +unsigned int func_write(int addr,unsigned long value){ + int fd; + int ret; + struct fpga_reg_data fpga_reg; + + fd = open(DEV_CHAR_FILENAME, O_RDWR); + + fpga_reg.reg = addr; + fpga_reg.value = value; + + ioctl(fd, WRITEREG, (void *)&fpga_reg); + + close(fd); + return 0; +} + +unsigned int func_read(int addr){ + int fd; + int ret; + + struct fpga_reg_data fpga_reg; + + fd = open(DEV_CHAR_FILENAME, O_RDWR); + + fpga_reg.reg = addr; + + ioctl(fd, READREG, (void *)&fpga_reg); + + close(fd); + return fpga_reg.value; +} + +void dump_status(int Stat){ + debug("#########################"); + debug("%d ready(1)/busy(0)", (Stat&SPI_STAT_MARK_READY)!=0); + debug("%d done", (Stat&SPI_STAT_MARK_DONE)!=0); + debug("%d error any", (Stat&SPI_STAT_MARK_ERROR_ANY)!=0); + debug("%d error checkId", (Stat&SPI_STAT_MARK_ERROR_CHKID)!=0); + debug("%d error erase", (Stat&SPI_STAT_MARK_ERROR_ERASE)!=0); + debug("%d error program", (Stat&SPI_STAT_MARK_ERROR_PROG)!=0); + debug("%d error timeout", (Stat&SPI_STAT_MARK_ERROR_TOUT)!=0); + debug("%d error crc", (Stat&SPI_STAT_MARK_ERROR_CRC)!=0); + debug("%d stage started", (Stat&SPI_STAT_MARK_STG_STARTED)!=0); + debug("%d stage inited", (Stat&SPI_STAT_MARK_STG_INITED)!=0); + debug("%d stage checked id", (Stat&SPI_STAT_MARK_STG_CHECKED_ID)!=0); + debug("%d stage erasred", (Stat&SPI_STAT_MARK_STG_ERSD_SW)!=0); + debug("%d stage upload erase img", (Stat&SPI_STAT_MARK_STG_UP_ERSD_IMG)!=0); + debug("%d stage upload program img",(Stat&SPI_STAT_MARK_STG_UP_PRG_IMG)!=0); + debug("%d stage verified", (Stat&SPI_STAT_MARK_STG_VERIFIED)!=0); + debug("%d stage completed", (Stat&SPI_STAT_MARK_STG_PRG_CMPT)!=0); +} + +int flash_program(char *data,int lens){ + int ctimeout; + int error =0; + unsigned long Stat = 0; + + reg_read(REG_SPI_RESET,Stat); + printf("Read Reset is %x\n",Stat); + printf("Reset Module \n"); + reg_write(REG_SPI_RESET,0x1); // reset + sleep(1); + reg_write(REG_SPI_RESET,0x0); // normal mode + ctimeout=0; + do{ // wait for done flag + reg_read(REG_SPI_STAT,Stat); + if(Stat & SPI_STAT_MARK_ERROR_ANY){ + dump_status(Stat); + error = Stat; + break; + } + if(ctimeout++ > WAIT_WRITE_READY_SEC){ + error = Stat| SPI_STAT_MARK_ERROR_TOUT; + debug("wait ready timeout . . ."); + break; + } + printf(" waiting status to ready ... %d s. status = %x\n",ctimeout,Stat); + sleep(1); + }while((Stat & 0x80F8) != 0x80F8); + if(error){ + return -1; + } + printf("Ready\n"); + + + for(int i=0;i WAIT_WRITE_CONTINUE_CYCLE){ + error = Stat| SPI_STAT_MARK_ERROR_TOUT; + debug("wait ready timeout . . ."); + break; + } + }while((Stat & 0x80F8) != 0x80F8); + + if(error){ + printf("FPGA programing fail at %d/%d\n",i,lens); + debug("Status = %4.4X",error); + break; + } + + i +=4; + + if(i%(lens/40*4)==0){ + printf("FPGA programing . . . %d/%d\n",i,lens); + } + } + + dump_status(Stat); + printf("Status = %4.4X\n",Stat); + + reg_write(REG_SPI_WR_EN,0x0); // write protect + reg_write(REG_SPI_RESET,0x1); // module reset + + return error; +} + +int main(int argc,char **argv){ + FILE *pFILE; + int filesize; + char *filename; + char *fpga_buff; + int status; + int max_size = 128; + int current_size = max_size; + int i = 0; + int c = EOF; + + printf(" FPGA PROGRAMMNG version 0.1.1 \n"); + printf(" build date : %s %s\n",__DATE__,__TIME__); + + filename = NULL; + filename = malloc(max_size); + if(!filename){ + exit(-12); /* Out of memory */ + } + + if(argc<2){ + printf("please enter filename : "); + while((c = getchar()) != '\n' && c != EOF ){ + + filename[i++] = (char)c; + if(i == current_size){ + current_size += max_size; + filename = realloc(filename, current_size); + } + } + filename[i] = '\0'; + }else{ + i = strlen(argv[1]) + 1; + filename = realloc(filename, i); + strcpy(filename, argv[1]); + } + + pFILE = fopen(filename,"rb"); + free(filename); + if (pFILE == NULL) + { + printf("Could not open the file %s, exit\n",filename); + return -5; + } + + fseek(pFILE , 0 , SEEK_END); + filesize = ftell (pFILE); + rewind(pFILE); + fpga_buff = malloc(filesize); + if(fpga_buff==NULL){ + printf("Can't Allocate memory \n"); + return -5; + } + + fread(fpga_buff,1,filesize,pFILE); + fclose(pFILE); + + printf(" Start FPGA Flash ... \n"); + + status = flash_program(fpga_buff,filesize); + + if(status == 0){ + printf(" Programing finish \n"); + }else{ + printf(" Program Error : error code %4.4x \n",status); + } + + return status; +} diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/Makefile b/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/Makefile new file mode 100644 index 00000000000..022bcc566c6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/Makefile @@ -0,0 +1,25 @@ +CC = gcc +AR = ar +CFLAGS = -Wall -W -Wunused -lpthread -g -O2 -ggdb +#LFLAGS = -lm -pthread -DVME_DEBUG +LFLAGS = -lm -pthread +DEL_FILE = rm -f +MV_FILE = mv -f +OBJ_FILE_NODE = ./*.o +#VPATH = +INCPATH = -I../include/ +TARGET = ispvm + +OBJECTS += hardware.o ispvm_ui.o ivm_core.o + + +.c.o: + $(CC) -c $(CFLAGS) $(INCPATH) -o $@ $< +$(TARGET):$(OBJECTS) + $(CC) $(LFLAGS) -o $(TARGET) $(OBJECTS) + +clean: + -$(DEL_FILE) $(TARGET) + -$(DEL_FILE) $(OBJ_FILE_NODE) + + diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/hardware.c b/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/hardware.c new file mode 100644 index 00000000000..7a4d5f5456d --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/hardware.c @@ -0,0 +1,409 @@ +/********************************************************************************* +* Lattice Semiconductor Corp. Copyright 2000-2008 +* +* This is the hardware.c of ispVME V12.1 for JTAG programmable devices. +* All the functions requiring customization are organized into this file for +* the convinience of porting. +*********************************************************************************/ +/********************************************************************************* + * Revision History: + * + * 09/11/07 NN Type cast mismatch variables + * 09/24/07 NN Added calibration function. + * Calibration will help to determine the system clock frequency + * and the count value for one micro-second delay of the target + * specific hardware. + * Modified the ispVMDelay function + * Removed Delay Percent support + * Moved the sclock() function from ivm_core.c to hardware.c + *********************************************************************************/ +#include "vmopcode.h" +#include +#include +#include //for open() +#include //for close() +#include +#include +#include + +/******************************************************************************** +* Declaration of global variables +* +*********************************************************************************/ +static int devmem_c2, devmem_c5; +static void *portC2, *portC5; + +/** + * NOTE: Using only core GPIO here + * FIXME: This should move to config section and support both CORE and SUS region. + */ + +// unsigned long g_siIspPins = 0x00000000; /*Keeper of JTAG pin state*/ +unsigned short g_usCpu_Frequency = CPU_FREQ_MH_CONFIG; /*Enter your CPU frequency here, unit in MHz.*/ + +/********************************************************************************* +* This is the definition of the bit locations of each respective +* signal in the global variable g_siIspPins. +* +* NOTE: Users must add their own implementation here to define +* the bit location of the signal to target their hardware. +* The example below is for the Lattice download cable on +* on the parallel port. +* +*********************************************************************************/ + + +unsigned long g_ucPinTDI = GPIO_TDI_CONFIG; /* Pin nummber of TDI */ +unsigned long g_ucPinTCK = GPIO_TCK_CONFIG; /* Pin nummber of TCK */ +unsigned long g_ucPinTMS = GPIO_TMS_CONFIG; /* Pin nummber of TMS */ +unsigned long g_ucPinENABLE = GPIO_ENABLE_CONFIG; /* Pin nummber of ENABLE */ +unsigned long g_ucPinTRST = GPIO_TRST_CONFIG; /* Pin nummber of TRST */ +unsigned long g_ucPinTDO = GPIO_TDO_CONFIG; /* Pin nummber of TDO */ +unsigned long g_ucInPort = GP_LVL; /* All TCI,TDO,TMS,TCK are on same register */ +unsigned long g_ucOutPort = GP_LVL; /* All TCI,TDO,TMS,TCK are on same register */ + +/* For Denverton CPU */ +// const unsigned long g_ucPinTDI = DNV_GPIO_TDI_CONFIG; +// const unsigned long g_ucPinTCK = DNV_GPIO_TCK_CONFIG; +// const unsigned long g_ucPinTMS = DNV_GPIO_TMS_CONFIG; +// const unsigned long g_ucPinTDO = DNV_GPIO_TDO_CONFIG; + +/*************************************************************** +* +* Functions declared in hardware.c module. +* +***************************************************************/ +void writePort( unsigned long a_ucPins, unsigned char a_ucValue ); +unsigned char readPort(); +void sclock(); +void ispVMDelay( unsigned short a_usTimeDelay ); +void calibration(void); + +/******************************************************************************** +* writePort +* To apply the specified value to the pins indicated. This routine will +* be modified for specific systems. +* As an example, this code uses the IBM-PC standard Parallel port, along with the +* schematic shown in Lattice documentation, to apply the signals to the +* JTAG pins. +* +* PC Parallel port pin Signal name Port bit address +* 2 g_ucPinTDI 1 +* 3 g_ucPinTCK 2 +* 4 g_ucPinTMS 4 +* 5 g_ucPinENABLE 8 +* 6 g_ucPinTRST 16 +* 10 g_ucPinTDO 64 +* +* Parameters: +* - a_ucPins, which is actually a set of bit flags (defined above) +* that correspond to the bits of the data port. Each of the I/O port +* bits that drives an isp programming pin is assigned a flag +* (through a #define) corresponding to the signal it drives. To +* change the value of more than one pin at once, the flags are added +* together, much like file access flags are. +* +* The bit flags are only set if the pin is to be changed. Bits that +* do not have their flags set do not have their levels changed. The +* state of the port is always manintained in the static global +* variable g_siIspPins, so that each pin can be addressed individually +* without disturbing the others. +* +* - a_ucValue, which is either HIGH (0x01 ) or LOW (0x00 ). Only these two +* values are valid. Any non-zero number sets the pin(s) high. +* +*********************************************************************************/ + +void writePort( unsigned long a_ucPins, unsigned char a_ucValue ) +{ + + unsigned long siIspPins = 0; + + /* For Denverton */ + // isp_dnv_gpio_write(a_ucPins, (unsigned int) a_ucValue); + + /* TODO: Convert to bit read/write function */ + siIspPins = inl_p( g_ucOutPort ); + if( a_ucValue ){ + siIspPins |= (1U << a_ucPins); + }else{ + siIspPins &= ~(1U << a_ucPins); + } + outl_p(siIspPins, g_ucOutPort); +} + +/********************************************************************************* +* +* readPort +* +* Returns the value of the TDO from the device. +* +**********************************************************************************/ +unsigned char readPort() +{ + unsigned char ucRet = 0; + + /* For Denverton */ + // if ( isp_dnv_gpio_read(DNV_GPIO_TDO_CONFIG) ) { + // ucRet = 0x01; + // } + // else { + // ucRet = 0x00; + // } + + /* TODO: Convert to bit read/write function */ + if ( inl_p( g_ucInPort ) & (1U << g_ucPinTDO)) { + ucRet = 0x01; + } + else { + ucRet = 0x00; + } + return ( ucRet ); +} + +/********************************************************************************* +* sclock +* +* Apply a pulse to TCK. +* +* This function is located here so that users can modify to slow down TCK if +* it is too fast (> 25MHZ). Users can change the IdleTime assignment from 0 to +* 1, 2... to effectively slowing down TCK by half, quarter... +* +*********************************************************************************/ +void sclock() +{ + unsigned short IdleTime = 0; //change to > 0 if need to slow down TCK + unsigned short usIdleIndex = 0; + IdleTime++; + for ( usIdleIndex = 0; usIdleIndex < IdleTime; usIdleIndex++ ) { + writePort( g_ucPinTCK, 0x01 ); + } + for ( usIdleIndex = 0; usIdleIndex < IdleTime; usIdleIndex++ ) { + writePort( g_ucPinTCK, 0x00 ); + } +} +/******************************************************************************** +* +* ispVMDelay +* +* +* Users must implement a delay to observe a_usTimeDelay, where +* bit 15 of the a_usTimeDelay defines the unit. +* 1 = milliseconds +* 0 = microseconds +* Example: +* a_usTimeDelay = 0x0001 = 1 microsecond delay. +* a_usTimeDelay = 0x8001 = 1 millisecond delay. +* +* This subroutine is called upon to provide a delay from 1 millisecond to a few +* hundreds milliseconds each time. +* It is understood that due to a_usTimeDelay is defined as unsigned short, a 16 bits +* integer, this function is restricted to produce a delay to 64000 micro-seconds +* or 32000 milli-second maximum. The VME file will never pass on to this function +* a delay time > those maximum number. If it needs more than those maximum, the VME +* file will launch the delay function several times to realize a larger delay time +* cummulatively. +* It is perfectly alright to provide a longer delay than required. It is not +* acceptable if the delay is shorter. +* +* Delay function example--using the machine clock signal of the native CPU------ +* When porting ispVME to a native CPU environment, the speed of CPU or +* the system clock that drives the CPU is usually known. +* The speed or the time it takes for the native CPU to execute one for loop +* then can be calculated as follows: +* The for loop usually is compiled into the ASSEMBLY code as shown below: +* LOOP: DEC RA; +* JNZ LOOP; +* If each line of assembly code needs 4 machine cycles to execute, +* the total number of machine cycles to execute the loop is 2 x 4 = 8. +* Usually system clock = machine clock (the internal CPU clock). +* Note: Some CPU has a clock multiplier to double the system clock for + the machine clock. +* +* Let the machine clock frequency of the CPU be F, or 1 machine cycle = 1/F. +* The time it takes to execute one for loop = (1/F ) x 8. +* Or one micro-second = F(MHz)/8; +* +* Example: The CPU internal clock is set to 100Mhz, then one micro-second = 100/8 = 12 +* +* The C code shown below can be used to create the milli-second accuracy. +* Users only need to enter the speed of the cpu. +* +**********************************************************************************/ +void ispVMDelay( unsigned short a_usTimeDelay ) +{ + unsigned short loop_index = 0; + unsigned short ms_index = 0; + unsigned short us_index = 0; + + if ( a_usTimeDelay & 0x8000 ) /*Test for unit*/ + { + a_usTimeDelay &= ~0x8000; /*unit in milliseconds*/ + } + else { /*unit in microseconds*/ + a_usTimeDelay = (unsigned short) (a_usTimeDelay/1000); /*convert to milliseconds*/ + if ( a_usTimeDelay <= 0 ) { + a_usTimeDelay = 1; /*delay is 1 millisecond minimum*/ + } + } + /*Users can replace the following section of code by their own*/ + for( ms_index = 0; ms_index < a_usTimeDelay; ms_index++) + { + /*Loop 1000 times to produce the milliseconds delay*/ + for (us_index = 0; us_index < 1000; us_index++) + { /*each loop should delay for 1 microsecond or more.*/ + loop_index = 0; + do { + /*The NOP fakes the optimizer out so that it doesn't toss out the loop code entirely*/ + asm("nop"); + }while (loop_index++ < ((g_usCpu_Frequency/8)+(+ ((g_usCpu_Frequency % 8) ? 1 : 0))));/*use do loop to force at least one loop*/ + } + } +} + +/********************************************************************************* +* +* calibration +* +* It is important to confirm if the delay function is indeed providing +* the accuracy required. Also one other important parameter needed +* checking is the clock frequency. +* Calibration will help to determine the system clock frequency +* and the loop_per_micro value for one micro-second delay of the target +* specific hardware. +* +**********************************************************************************/ +void calibration(void) +{ + /*Apply 2 pulses to TCK.*/ + writePort( g_ucPinTCK, 0x00 ); + writePort( g_ucPinTCK, 0x01 ); + writePort( g_ucPinTCK, 0x00 ); + writePort( g_ucPinTCK, 0x01 ); + writePort( g_ucPinTCK, 0x00 ); + + /*Delay for 1 millisecond. Pass on 1000 or 0x8001 both = 1ms delay.*/ + ispVMDelay(0x8001); + + /*Apply 2 pulses to TCK*/ + writePort( g_ucPinTCK, 0x01 ); + writePort( g_ucPinTCK, 0x00 ); + writePort( g_ucPinTCK, 0x01 ); + writePort( g_ucPinTCK, 0x00 ); + + ispVMDelay(0x8001); +} + +void port_test(void) +{ + int siRetCode; + unsigned char cbit; + + printf("TDI set HIGH.\n"); + if(scanf("%d",&siRetCode)){} + writePort( g_ucPinTDI, 0x01); + printf("TDI set LOW.\n"); + if(scanf("%d",&siRetCode)){} + writePort( g_ucPinTDI, 0x00); + printf("TMS set HIGH.\n"); + if(scanf("%d",&siRetCode)){} + writePort(g_ucPinTMS, 0x01); + printf("TMS set LOW.\n"); + if(scanf("%d",&siRetCode)){} + writePort(g_ucPinTMS, 0x00); + printf("TCK set HIGH.\n"); + if(scanf("%d",&siRetCode)){} + writePort(g_ucPinTCK, 0x01); + printf("TCK set LOW.\n"); + if(scanf("%d",&siRetCode)){} + writePort(g_ucPinTCK, 0x00); + printf("write finished.read begin:\n"); + if(scanf("%d",&siRetCode)){} + cbit = readPort(); + printf("Read date is %d\n", cbit); + printf("read begin:\n"); + if(scanf("%d",&siRetCode)){} + cbit = readPort(); + printf("Read date is %d\n", cbit); + printf("read finished.\n"); + if(scanf("%d",&siRetCode)){} +} + + +void isp_dnv_gpio_config(unsigned int gpio, unsigned int dir) +{ + volatile unsigned int *buffer; + // Select community + if(GET_PORT(gpio) == 0xC5){ + buffer = (volatile unsigned int *)(portC5 + OFFSET_ADDR(gpio)); + }else{ + buffer = (volatile unsigned int *)(portC2 + OFFSET_ADDR(gpio)); + } + // set mode to GPIO, set pin direction. + *buffer &= (~((unsigned int)7)) << 10; // clear [12:10] + *buffer &= (~((unsigned int)3)) << 8; // clear [9:8] + *buffer |= ((unsigned int)dir & 0x3) << 8; // set [9:8] +} + +void isp_dnv_gpio_write(unsigned int gpio, unsigned int value) +{ + volatile unsigned char *buffer; + // Select community + if(GET_PORT(gpio) == 0xC5){ + buffer = (volatile unsigned char *)(portC5 + OFFSET_ADDR(gpio)); + }else{ + buffer = (volatile unsigned char *)(portC2 + OFFSET_ADDR(gpio)); + } + if(value) { + *buffer = DNV_GPIO_LVL_HIGH; + } else { + *buffer = DNV_GPIO_LVL_LOW; + } +} + +int isp_dnv_gpio_read(unsigned int gpio) +{ + volatile unsigned int *buffer; + // Select community + if(GET_PORT(gpio) == 0xC5){ + buffer = (volatile unsigned int *)(portC5 + OFFSET_ADDR(gpio)); + }else{ + buffer = (volatile unsigned int *)(portC2 + OFFSET_ADDR(gpio)); + } + return (int)((*buffer & 0x2) >> 1); +} + + +void isp_dnv_gpio_init(void){ + + devmem_c2 = open("/dev/mem", O_RDWR | O_SYNC); + if (devmem_c2 == -1){ + perror("Can't open /dev/mem."); + return; + } + + devmem_c5 = open("/dev/mem", O_RDWR | O_SYNC); + if (devmem_c5 == -1){ + perror("Can't open /dev/mem."); + return; + } + + portC2 = mmap(NULL, MAP_SIZE(g_ucPinTCK) , PROT_READ | PROT_WRITE, MAP_SHARED, devmem_c2, g_ucPinTCK & ~MAP_MASK); + if (portC2 == MAP_FAILED) { + perror("Can't map memory: "); + return; + } + portC5 = mmap(NULL, MAP_SIZE(g_ucPinTDO) , PROT_READ | PROT_WRITE, MAP_SHARED, devmem_c5, g_ucPinTDO & ~MAP_MASK); + if (portC2 == MAP_FAILED) { + perror("Can't map memory: "); + return; + } +} + +void isp_dnv_gpio_deinit(void){ + munmap(portC2, MAP_SIZE(g_ucPinTCK)); + munmap(portC5, MAP_SIZE(g_ucPinTDO)); + close(devmem_c2); + close(devmem_c5); +} diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/ispvm_ui.c b/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/ispvm_ui.c new file mode 100644 index 00000000000..582117a6e2c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/ispvm_ui.c @@ -0,0 +1,908 @@ +/************************************************************** +* +* Lattice Semiconductor Corp. Copyright 2008 +* +* ispVME Embedded allows programming of Lattice's suite of FPGA +* devices on embedded systems through the JTAG port. The software +* is distributed in source code form and is open to re - distribution +* and modification where applicable. +* +* ispVME Embedded C Source comprised with 3 modules: +* ispvm_ui.c is the module provides input and output support. +* ivm_core.c is the module interpret the VME file(s). +* hardware.c is the module access the JTAG port of the device(s). +* +* The optional module cable.c is for supporting Lattice's parallel +* port ispDOWNLOAD cable on DOS and Windows 95/98 O/S. It can be +* requested from Lattice's ispVMSupport. +* +***************************************************************/ + + +/************************************************************** +* +* Revision History of ispvm_ui.c +* +* 3/6/07 ht Added functions vme_out_char(),vme_out_hex(), +* vme_out_string() to provide output resources. +* Consolidate all printf() calls into the added output +* functions. +* +* 09/11/07 NN Added Global variables initialization +* 09/24/07 NN Added a switch allowing users to do calibration. +* Calibration will help to determine the system clock frequency +* and the count value for one micro-second delay of the target +* specific hardware. +* Removed Delay Percent support +* 11/15/07 NN moved the checking of the File CRC to the end of processing +* 08/28/08 NN Added Calculate checksum support. +***************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include "vmopcode.h" + +/*************************************************************** +* +* File pointer to the VME file. +* +***************************************************************/ + +FILE * g_pVMEFile = NULL; + +#define VME_DEBUG + +#define DEBUG +#ifdef DEBUG +#define Debug_printf(fmt, args...) printf(fmt, ##args); +#else +#define Debug_printf(fmt, args...) +#endif + +/*************************************************************** +* +* Functions declared in this ispvm_ui.c module +* +***************************************************************/ +unsigned char GetByte(void); +void vme_out_char(unsigned char charOut); +void vme_out_hex(unsigned char hexOut); +void vme_out_string(char *stringOut); +void ispVMMemManager( signed char cTarget, unsigned short usSize ); +void ispVMFreeMem(void); +void error_handler( short a_siRetCode, char * pszMessage ); +signed char ispVM( const char * a_pszFilename ); +long isp_vme_file_size_get(void); +int isp_vme_file_size_set(char *file_name); +int isp_print_progess_bar(long pec); +void print_usage(char *app_name); +/*************************************************************** +* +* Global variables. +* +***************************************************************/ +unsigned short g_usPreviousSize = 0; +unsigned short g_usExpectedCRC = 0; +static unsigned long vme_file_size = 0; + +/*************************************************************** +* +* External variables and functions declared in ivm_core.c module. +* +***************************************************************/ +extern signed char ispVMCode(); +extern void ispVMCalculateCRC32( unsigned char a_ucData ); +extern void ispVMStart(); +extern void ispVMEnd(); +extern unsigned short g_usCalculatedCRC; +extern unsigned short g_usDataType; +extern unsigned char * g_pucOutMaskData, + * g_pucInData, + * g_pucOutData, + * g_pucHIRData, + * g_pucTIRData, + * g_pucHDRData, + * g_pucTDRData, + * g_pucOutDMaskData, + * g_pucIntelBuffer; +extern unsigned char * g_pucHeapMemory; +extern unsigned short g_iHeapCounter; +extern unsigned short g_iHEAPSize; +extern unsigned short g_usIntelDataIndex; +extern unsigned short g_usIntelBufferSize; +extern LVDSPair * g_pLVDSList; +//08/28/08 NN Added Calculate checksum support. +extern unsigned long g_usChecksum; +extern unsigned int g_uiChecksumIndex; +/*************************************************************** +* +* External variables and functions declared in hardware.c module. +* +***************************************************************/ +extern void calibration(void); +extern void writePort( unsigned long a_ucPins, unsigned char a_ucValue ); +extern unsigned short g_usCpu_Frequency; +extern unsigned long g_ucInPort; +extern unsigned long g_ucOutPort; + +/*************************************************************** +* +* Supported VME versions. +* +***************************************************************/ + +const char * const g_szSupportedVersions[] = { "__VME2.0", "__VME3.0", "____12.0", "____12.1", 0 }; + + +/*************************************************************** +* +* GetByte +* +* Returns a byte to the caller. The returned byte depends on the +* g_usDataType register. If the HEAP_IN bit is set, then the byte +* is returned from the HEAP. If the LHEAP_IN bit is set, then +* the byte is returned from the intelligent buffer. Otherwise, +* the byte is returned directly from the VME file. +* +***************************************************************/ + +unsigned char GetByte() +{ + unsigned char ucData = 0; + /* Prepare progress bar calculation */ + static long offset = 0; + int pec = 0; + long file_size = isp_vme_file_size_get(); + int bytes_pec = (file_size + 99) / 100; + + if ( g_usDataType & HEAP_IN ) { + + /*************************************************************** + * + * Get data from repeat buffer. + * + ***************************************************************/ + + if ( g_iHeapCounter > g_iHEAPSize ) { + + /*************************************************************** + * + * Data over-run. + * + ***************************************************************/ + + return 0xFF; + } + + ucData = g_pucHeapMemory[ g_iHeapCounter++ ]; + } + else if ( g_usDataType & LHEAP_IN ) { + + /*************************************************************** + * + * Get data from intel buffer. + * + ***************************************************************/ + + if ( g_usIntelDataIndex >= g_usIntelBufferSize ) { + + /*************************************************************** + * + * Data over-run. + * + ***************************************************************/ + + return 0xFF; + } + + ucData = g_pucIntelBuffer[ g_usIntelDataIndex++ ]; + } + else { + + /*************************************************************** + * + * Get data from file. + * + ***************************************************************/ + + ucData = (unsigned char)fgetc( g_pVMEFile ); + /* Update the progress bar */ + pec = ++offset / bytes_pec; + if(offset <= (pec * bytes_pec)) + isp_print_progess_bar(pec); + else if(offset >= (file_size - 2)) + isp_print_progess_bar(100); + if ( feof( g_pVMEFile ) ) { + + /*************************************************************** + * + * Reached EOF. + * + ***************************************************************/ + + return 0xFF; + } + /*************************************************************** + * + * Calculate the 32-bit CRC if the expected CRC exist. + * + ***************************************************************/ + if( g_usExpectedCRC != 0) + { + ispVMCalculateCRC32(ucData); + } + } + + return ( ucData ); +} + +/*************************************************************** +* +* vme_out_char +* +* Send a character out to the output resource if available. +* The monitor is the default output resource. +* +* +***************************************************************/ +void vme_out_char(unsigned char charOut) +{ + printf("%c",charOut); +} +/*************************************************************** +* +* vme_out_hex +* +* Send a character out as in hex format to the output resource +* if available. The monitor is the default output resource. +* +* +***************************************************************/ +void vme_out_hex(unsigned char hexOut) +{ + printf("%.2X",hexOut); +} +/*************************************************************** +* +* vme_out_string +* +* Send a text string out to the output resource if available. +* The monitor is the default output resource. +* +* +***************************************************************/ +void vme_out_string(char *stringOut) +{ + if(stringOut) + { + printf("%s",stringOut); + } + +} +/*************************************************************** +* +* ispVMMemManager +* +* Allocate memory based on cTarget. The memory size is specified +* by usSize. +* +***************************************************************/ + +void ispVMMemManager( signed char cTarget, unsigned short usSize ) +{ + switch ( cTarget ) { + case XTDI: + case TDI: + if ( g_pucInData != NULL ) { + if ( g_usPreviousSize == usSize ) {/*memory exist*/ + break; + } + else { + free( g_pucInData ); + g_pucInData = NULL; + } + } + g_pucInData = ( unsigned char * ) malloc( usSize / 8 + 2 ); + g_usPreviousSize = usSize; + /* FALLTHRU */ + case XTDO: + case TDO: + if ( g_pucOutData!= NULL ) { + if ( g_usPreviousSize == usSize ) { /*already exist*/ + break; + } + else { + free( g_pucOutData ); + g_pucOutData = NULL; + } + } + g_pucOutData = ( unsigned char * ) malloc( usSize / 8 + 2 ); + g_usPreviousSize = usSize; + break; + case MASK: + if ( g_pucOutMaskData != NULL ) { + if ( g_usPreviousSize == usSize ) {/*already allocated*/ + break; + } + else { + free( g_pucOutMaskData ); + g_pucOutMaskData = NULL; + } + } + g_pucOutMaskData = ( unsigned char * ) malloc( usSize / 8 + 2 ); + g_usPreviousSize = usSize; + break; + case HIR: + if ( g_pucHIRData != NULL ) { + free( g_pucHIRData ); + g_pucHIRData = NULL; + } + g_pucHIRData = ( unsigned char * ) malloc( usSize / 8 + 2 ); + break; + case TIR: + if ( g_pucTIRData != NULL ) { + free( g_pucTIRData ); + g_pucTIRData = NULL; + } + g_pucTIRData = ( unsigned char * ) malloc( usSize / 8 + 2 ); + break; + case HDR: + if ( g_pucHDRData != NULL ) { + free( g_pucHDRData ); + g_pucHDRData = NULL; + } + g_pucHDRData = ( unsigned char * ) malloc( usSize / 8 + 2 ); + break; + case TDR: + if ( g_pucTDRData != NULL ) { + free( g_pucTDRData ); + g_pucTDRData = NULL; + } + g_pucTDRData = ( unsigned char * ) malloc( usSize / 8 + 2 ); + break; + case HEAP: + if ( g_pucHeapMemory != NULL ) { + free( g_pucHeapMemory ); + g_pucHeapMemory = NULL; + } + g_pucHeapMemory = ( unsigned char * ) malloc( usSize + 2 ); + break; + case DMASK: + if ( g_pucOutDMaskData != NULL ) { + if ( g_usPreviousSize == usSize ) { /*already allocated*/ + break; + } + else { + free( g_pucOutDMaskData ); + g_pucOutDMaskData = NULL; + } + } + g_pucOutDMaskData = ( unsigned char * ) malloc( usSize / 8 + 2 ); + g_usPreviousSize = usSize; + break; + case LHEAP: + if ( g_pucIntelBuffer != NULL ) { + free( g_pucIntelBuffer ); + g_pucIntelBuffer = NULL; + } + g_pucIntelBuffer = ( unsigned char * ) malloc( usSize + 2 ); + break; + case LVDS: + if ( g_pLVDSList != NULL ) { + free( g_pLVDSList ); + g_pLVDSList = NULL; + } + g_pLVDSList = ( LVDSPair * ) calloc( usSize, sizeof( LVDSPair ) ); + break; + default: + return; + } +} + +/*************************************************************** +* +* ispVMFreeMem +* +* Free memory that were dynamically allocated. +* +***************************************************************/ + +void ispVMFreeMem() +{ + if ( g_pucHeapMemory != NULL ) { + free( g_pucHeapMemory ); + g_pucHeapMemory = NULL; + } + + if ( g_pucOutMaskData != NULL ) { + free( g_pucOutMaskData ); + g_pucOutMaskData = NULL; + } + + if ( g_pucInData != NULL ) { + free( g_pucInData ); + g_pucInData = NULL; + } + + if ( g_pucOutData != NULL ) { + free( g_pucOutData ); + g_pucOutData = NULL; + } + + if ( g_pucHIRData != NULL ) { + free( g_pucHIRData ); + g_pucHIRData = NULL; + } + + if ( g_pucTIRData != NULL ) { + free( g_pucTIRData ); + g_pucTIRData = NULL; + } + + if ( g_pucHDRData != NULL ) { + free( g_pucHDRData ); + g_pucHDRData = NULL; + } + + if ( g_pucTDRData != NULL ) { + free( g_pucTDRData ); + g_pucTDRData = NULL; + } + + if ( g_pucOutDMaskData != NULL ) { + free( g_pucOutDMaskData ); + g_pucOutDMaskData = NULL; + } + + if ( g_pucIntelBuffer != NULL ) { + free( g_pucIntelBuffer ); + g_pucIntelBuffer = NULL; + } + + if ( g_pLVDSList != NULL ) { + free( g_pLVDSList ); + g_pLVDSList = NULL; + } +} + +/*************************************************************** +* +* error_handler +* +* Reports the error message. +* +***************************************************************/ + +void error_handler( short a_siRetCode, char * pszMessage ) +{ + const char * pszErrorMessage[] = { "pass", + "verification fail", + "can't find the file", + "wrong file type", + "file error", + "option error", + "crc verification error" }; + + strcpy( pszMessage, pszErrorMessage[ -a_siRetCode ] ); +} +/*************************************************************** +* +* ispVM +* +* The entry point of the ispVM embedded. If the version and CRC +* are verified, then the VME will be processed. +* +***************************************************************/ + +signed char ispVM( const char * a_pszFilename ) +{ + char szFileVersion[ 9 ] = { 0 }; + signed char cRetCode = 0; + signed char cIndex = 0; + signed char cVersionIndex = 0; + unsigned char ucReadByte = 0; + + /*************************************************************** + * + * Global variables initialization. + * + * 09/11/07 NN Added + ***************************************************************/ + g_pucHeapMemory = NULL; + g_iHeapCounter = 0; + g_iHEAPSize = 0; + g_usIntelDataIndex = 0; + g_usIntelBufferSize = 0; + g_usPreviousSize = 0; + + /*************************************************************** + * + * Open a file pointer to the VME file. + * + ***************************************************************/ + + if ( ( g_pVMEFile = fopen( a_pszFilename, "rb" ) ) == NULL ) { + return VME_FILE_READ_FAILURE; + } + g_usCalculatedCRC = 0; + g_usExpectedCRC = 0; + ucReadByte = GetByte(); + switch( ucReadByte ) { + case FILE_CRC: + + /*************************************************************** + * + * Read and store the expected CRC to do the comparison at the end. + * Only versions 3.0 and higher support CRC protection. + * + ***************************************************************/ + + g_usExpectedCRC = (unsigned char ) fgetc( g_pVMEFile ); + g_usExpectedCRC <<= 8; + g_usExpectedCRC |= fgetc( g_pVMEFile ); + + + /*************************************************************** + * + * Read and store the version of the VME file. + * + ***************************************************************/ + + for ( cIndex = 0; cIndex < 8; cIndex++ ) { + szFileVersion[ cIndex ] = GetByte(); + } + + break; + default: + + /*************************************************************** + * + * Read and store the version of the VME file. Must be version 2.0. + * + ***************************************************************/ + + szFileVersion[ 0 ] = ( signed char ) ucReadByte; + for ( cIndex = 1; cIndex < 8; cIndex++ ) { + szFileVersion[ cIndex ] = GetByte(); + } + + break; + } + + /*************************************************************** + * + * Compare the VME file version against the supported version. + * + ***************************************************************/ + for ( cVersionIndex = 0; g_szSupportedVersions[ cVersionIndex ] != 0; cVersionIndex++ ) { + for ( cIndex = 0; cIndex < 8; cIndex++ ) { + if ( szFileVersion[ cIndex ] != g_szSupportedVersions[ cVersionIndex ][ cIndex ] ) { + cRetCode = VME_VERSION_FAILURE; + break; + } + cRetCode = 0; + } + + if ( cRetCode == 0 ) { + + /*************************************************************** + * + * Found matching version, break. + * + ***************************************************************/ + + break; + } + } + + if ( cRetCode < 0 ) { + + /*************************************************************** + * + * VME file version failed to match the supported versions. + * + ***************************************************************/ + + fclose( g_pVMEFile ); + g_pVMEFile = NULL; + return VME_VERSION_FAILURE; + } + + /*************************************************************** + * + * Enable the JTAG port to communicate with the device. + * Set the JTAG state machine to the Test-Logic/Reset State. + * + ***************************************************************/ + + ispVMStart(); + + /*************************************************************** + * + * Process the VME file. + * + ***************************************************************/ + + cRetCode = ispVMCode(); + + /*************************************************************** + * + * Set the JTAG State Machine to Test-Logic/Reset state then disable + * the communication with the JTAG port. + * + ***************************************************************/ + + ispVMEnd(); + + fclose( g_pVMEFile ); + g_pVMEFile = NULL; + + + ispVMFreeMem(); + + /*************************************************************** + * + * Compare the expected CRC versus the calculated CRC. + * + ***************************************************************/ + + if ( cRetCode == 0 && g_usExpectedCRC != 0 && ( g_usExpectedCRC != g_usCalculatedCRC ) ) { + printf( "Expected CRC: 0x%.4X\n", g_usExpectedCRC ); + printf( "Calculated CRC: 0x%.4X\n", g_usCalculatedCRC ); + return VME_CRC_FAILURE; + } + + return ( cRetCode ); +} + +// inline char *strlwr(char *str) +// { +// char *orig = str; + +// for (; *str != '\0'; str++) +// *str = tolower(*str); + +// return orig; +// } + +int isp_vme_file_size_set(char *file_name) +{ + struct stat statbuf; + + stat(file_name, &statbuf); + vme_file_size = statbuf.st_size; + + return 0; +} + +long isp_vme_file_size_get(void) +{ + return vme_file_size; +} + +int isp_print_progess_bar(long pec) +{ + int i = 0; + + printf("["); + for(i = 0; i < (pec / 2); i++) { + printf("="); + } + for(i = pec / 2; i < 50; i++) { + printf(" "); + } + printf("]"); + printf(" [%ld%%]\r", pec); + fflush(stdout); + if(pec == 100) + printf("\n"); + + return 0; +} + +void print_usage(char *app_name){ + printf(" usage: %s [options] [filename]\n", app_name); + printf(" Options:\n"); + printf(" -h : to print this message.\n"); + printf(" -c : to select the JTAG chain 0,1,2\n"); + printf(" default is at 0.\n"); + printf(" -f : to specify CPU clock frequency in MHz.\n"); +} + +/*************************************************************** +* +* main +* +***************************************************************/ +int main( int argc, char * argv[] ) +{ + short siRetCode = 0; + short sicalibrate = 1; + short setCpuFrequency = 0; + + char *cpld_img = "cpld.vme"; + int JTAG_chain = 0; + int option; + //08/28/08 NN Added Calculate checksum support. + g_usChecksum = 0; + g_uiChecksumIndex = 0; + + vme_out_string( " Lattice Semiconductor Corp.\n" ); + vme_out_string( "\n ispVME(tm) V"); + vme_out_string( VME_VERSION_NUMBER ); + vme_out_string(" Copyright 1998-2011.\n"); + vme_out_string( "\nFor daisy chain programming of all in-system programmable devices\n" ); + vme_out_string( "\nCLS internal version 1.1.0 for questone2bd.\n\n" ); + + while( ( option = getopt(argc, argv, "f:c:h")) != -1 ){ + switch (option){ + case 'h': + print_usage(argv[0]); + return 0; + case 'c': + // set JTAG chain number + JTAG_chain = atoi(optarg); + break; + case 'f': + // set CPU frequency + g_usCpu_Frequency = atoi(optarg); + setCpuFrequency = 1; + break; + case '?': + print_usage(argv[0]); + return -1; + } + } + + if( argc - optind ) + cpld_img = argv[optind]; + + if( JTAG_chain < 0 || JTAG_chain > 2 ){ + //print usage and return error + printf("Invalid JTAG chain specify: %d\n", JTAG_chain); + print_usage(argv[0]); + return -1; + } + + if( g_usCpu_Frequency <= 0 && setCpuFrequency ){ + //print usage and return error + printf("Invalid CPU frequency specify: %d\n", g_usCpu_Frequency); + print_usage(argv[0]); + return -1; + } + + if (iopl(3)) + { + perror("iopl"); + exit(1);/* reminder here: do not use "return", I warned */ + } + else + { + + /* For Denvertion CPU */ + // isp_dnv_gpio_init(); + // isp_dnv_gpio_config(GPIO_TCK_CONFIG, GPIO_DIR_OUTPUT); + // isp_dnv_gpio_config(GPIO_TMS_CONFIG, GPIO_DIR_OUTPUT); + // isp_dnv_gpio_config(GPIO_TDI_CONFIG, GPIO_DIR_OUTPUT); + // isp_dnv_gpio_config(GPIO_TDO_CONFIG, GPIO_DIR_INPUT); + + + /* TODO: Convert to bit read/write function */ + // Set ICHx GPIO_USE_SEL of TDI,TDO,TMS,TCK,GPIO14 + unsigned long data = 0; + data = inl_p(GPIO_USE_SEL); + data |= (1U << GPIO_TCK_CONFIG); + data |= (1U << GPIO_TMS_CONFIG); + data |= (1U << GPIO_TDI_CONFIG); + data |= (1U << GPIO_TDO_CONFIG); + data |= (1U << 14); + outl_p(data, GPIO_USE_SEL); + // Set ICHx GP_IO_SEL of TDI,TDO,TMS,TCK,GPIO14 + data = inl_p(GP_IO_SEL); + data &= ~(1U << GPIO_TCK_CONFIG); + data &= ~(1U << GPIO_TMS_CONFIG); + data &= ~(1U << GPIO_TDI_CONFIG); + data &= ~(1U << 14); + data |= (1U << GPIO_TDO_CONFIG); + outl_p(data, GP_IO_SEL); + + // Set ICHx GPIO_USE_SEL of GPIO70 + data = inl_p(GPIO_USE_SEL3); + data |= (1U << 6); + outl_p(data, GPIO_USE_SEL3); + // Set ICHx GP_IO_SEL of GPIO70 + data = inl_p(GP_IO_SEL3); + data &= ~(1U << 6); + outl_p(data, GP_IO_SEL3); + } + + /* FIXME: export and setting GPIO register bank on the fly could cause a bug. + * Plan to add the function to set/clear GPIO register bit for more sucure. + */ + /* Switch to control JTAG chain muxes */ + switch (JTAG_chain){ + case 0: + printf("Select main JTAG chain\n"); + // Set GPIO70 to Low + g_ucOutPort = GP_LVL3; + writePort( 6, 0x00 ); + break; + case 1: + printf("Select Top line card JTAG chain\n"); + // Ste GPIO70 to High + g_ucOutPort = GP_LVL3; + writePort( 6, 0x01 ); + // Ste GPIO14 to Low + g_ucOutPort = GP_LVL; + writePort( 14, 0x00 ); + break; + case 2: + printf("Select Buttom line card JTAG chain\n"); + // Ste GPIO70 to High + g_ucOutPort = GP_LVL3; + writePort( 6, 0x01 ); + // Ste GPIO14 to High + g_ucOutPort = GP_LVL; + writePort( 14, 0x01 ); + break; + } + + /* FIXME: This line is very important for TDI,TMS,TCK,TDO */ + // Set the register back to first bank! + g_ucOutPort = GP_LVL; + + printf("Set CPU frequency to %d MHz\n", g_usCpu_Frequency); + + siRetCode = 0; + if(sicalibrate) + { + vme_out_string ("calibration ....\n\n"); + calibration(); + } + + printf( "Processing virtual machine file ("); + printf( "%s",cpld_img); + printf(")......\n\n"); + isp_vme_file_size_set(cpld_img); + siRetCode = ispVM(cpld_img); + + /* Set JTAG chain muxes to default chain. */ + // Set GPIO70 to Low + g_ucOutPort = GP_LVL3; + writePort( 6, 0x00 ); + + /* For Denverton CPU */ + // isp_dnv_gpio_deinit(); + + if ( siRetCode < 0 ) { + vme_out_string( "Failed due to "); + printf( " return code %d\n\n", siRetCode); + vme_out_string( "+=======+\n" ); + vme_out_string( "| FAIL! |\n" ); + vme_out_string( "+=======+\n\n" ); + }else { + vme_out_string( "+=======+\n" ); + vme_out_string( "| PASS! |\n" ); + vme_out_string( "+=======+\n\n" ); + //08/28/08 NN Added Calculate checksum support. + if(g_usChecksum != 0) + { + g_usChecksum &= 0xFFFF; + printf("Data Checksum: %.4lx\n\n",g_usChecksum); + g_usChecksum = 0; + } + } + + if (iopl(0)) + { + perror("iopl"); + exit(1);/* reminder here: do not use "return", I warned */ + } + exit( siRetCode ); +} + diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/ivm_core.c b/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/ivm_core.c new file mode 100644 index 00000000000..fd6fa7b987c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/ivm_core.c @@ -0,0 +1,3081 @@ +/*************************************************************** + * + * Lattice Semiconductor Corp. Copyright 2009 + * + * ispVME Embedded allows programming of Lattice's suite of FPGA + * devices on embedded systems through the JTAG port. The software + * is distributed in source code form and is open to re - distribution + * and modification where applicable. + * + * Revision History of ivm_core.c module: + * 4/25/06 ht Change some variables from unsigned short or int + * to long int to make the code compiler independent. + * 5/24/06 ht Support using RESET (TRST) pin as a special purpose + * control pin such as triggering the loading of known + * state exit. + * 3/6/07 ht added functions to support output to terminals + * + * 09/24/07 NN Type cast mismatch variables + * Moved the sclock() function to hardware.c + * 08/28/08 NN Added Calculate checksum support. + * 4/1/09 Nguyen replaced the recursive function call codes on + * the ispVMLCOUNT function + * + ***************************************************************/ + +#include +#include +#include "vmopcode.h" + +/*************************************************************** + * + * Global variables used to specify the flow control and data type. + * + * g_usFlowControl: flow control register. Each bit in the + * register can potentially change the + * personality of the embedded engine. + * g_usDataType: holds the data type of the current row. + * + ***************************************************************/ + +unsigned short g_usFlowControl = 0x0000; +unsigned short g_usDataType = 0x0000; + +/*************************************************************** + * + * Global variables used to specify the ENDDR and ENDIR. + * + * g_ucEndDR: the state that the device goes to after SDR. + * g_ucEndIR: the state that the device goes to after SIR. + * + ***************************************************************/ + +unsigned char g_ucEndDR = DRPAUSE; +unsigned char g_ucEndIR = IRPAUSE; + +/*************************************************************** + * + * Global variables used to support header/trailer. + * + * g_usHeadDR: the number of lead devices in bypass. + * g_usHeadIR: the sum of IR length of lead devices. + * g_usTailDR: the number of tail devices in bypass. + * g_usTailIR: the sum of IR length of tail devices. + * + ***************************************************************/ + +unsigned short g_usHeadDR = 0; +unsigned short g_usHeadIR = 0; +unsigned short g_usTailDR = 0; +unsigned short g_usTailIR = 0; + +/*************************************************************** + * + * Global variable to store the number of bits of data or instruction + * to be shifted into or out from the device. + * + ***************************************************************/ + +unsigned short g_usiDataSize = 0; + +/*************************************************************** + * + * Stores the frequency. Default to 1 MHz. + * + ***************************************************************/ + +int g_iFrequency = 1000; + +/*************************************************************** + * + * Stores the maximum amount of ram needed to hold a row of data. + * + ***************************************************************/ + +unsigned short g_usMaxSize = 0; + +/*************************************************************** + * + * Stores the LSH or RSH value. + * + ***************************************************************/ + +unsigned short g_usShiftValue = 0; + +/*************************************************************** + * + * Stores the current repeat loop value. + * + ***************************************************************/ + +unsigned short g_usRepeatLoops = 0; + +/*************************************************************** + * + * Stores the current vendor. + * + ***************************************************************/ + +signed char g_cVendor = LATTICE; + +/*************************************************************** + * + * Stores the VME file CRC. + * + ***************************************************************/ + +unsigned short g_usCalculatedCRC = 0; + +/*************************************************************** + * + * Stores the Device Checksum. + * + ***************************************************************/ +//08/28/08 NN Added Calculate checksum support. +unsigned long g_usChecksum = 0; +unsigned int g_uiChecksumIndex = 0; + +/*************************************************************** + * + * Stores the current state of the JTAG state machine. + * + ***************************************************************/ + +signed char g_cCurrentJTAGState = 0; + +/*************************************************************** + * + * Global variables used to support looping. + * + * g_pucHeapMemory: holds the entire repeat loop. + * g_iHeapCounter: points to the current byte in the repeat loop. + * g_iHEAPSize: the current size of the repeat in bytes. + * + ***************************************************************/ + +unsigned char * g_pucHeapMemory = NULL; +unsigned short g_iHeapCounter = 0; +unsigned short g_iHEAPSize = 0; + +/*************************************************************** + * + * Global variables used to support intelligent programming. + * + * g_usIntelDataIndex: points to the current byte of the + * intelligent buffer. + * g_usIntelBufferSize: holds the size of the intelligent + * buffer. + * + ***************************************************************/ + +unsigned short g_usIntelDataIndex = 0; +unsigned short g_usIntelBufferSize = 0; + +/**************************************************************************** + * + * Holds the maximum size of each respective buffer. These variables are used + * to write the HEX files when converting VME to HEX. + * + *****************************************************************************/ + +unsigned short g_usTDOSize = 0; +unsigned short g_usMASKSize = 0; +unsigned short g_usTDISize = 0; +unsigned short g_usDMASKSize = 0; +unsigned short g_usLCOUNTSize = 0; +unsigned short g_usHDRSize = 0; +unsigned short g_usTDRSize = 0; +unsigned short g_usHIRSize = 0; +unsigned short g_usTIRSize = 0; +unsigned short g_usHeapSize = 0; + +/*************************************************************** + * + * Global variables used to store data. + * + * g_pucOutMaskData: local RAM to hold one row of MASK data. + * g_pucInData: local RAM to hold one row of TDI data. + * g_pucOutData: local RAM to hold one row of TDO data. + * g_pucHIRData: local RAM to hold the current SIR header. + * g_pucTIRData: local RAM to hold the current SIR trailer. + * g_pucHDRData: local RAM to hold the current SDR header. + * g_pucTDRData: local RAM to hold the current SDR trailer. + * g_pucIntelBuffer: local RAM to hold the current intelligent buffer. + * g_pucOutDMaskData: local RAM to hold one row of DMASK data. + * + ***************************************************************/ + +unsigned char * g_pucOutMaskData = NULL, + * g_pucInData = NULL, + * g_pucOutData = NULL, + * g_pucHIRData = NULL, + * g_pucTIRData = NULL, + * g_pucHDRData = NULL, + * g_pucTDRData = NULL, + * g_pucIntelBuffer = NULL, + * g_pucOutDMaskData = NULL; + +/*************************************************************** + * + * JTAG state machine transition table. + * + ***************************************************************/ + +struct { + unsigned char CurState; /* From this state */ + unsigned char NextState; /* Step to this state */ + unsigned char Pattern; /* The tragetory of TMS */ + unsigned char Pulses; /* The number of steps */ +} g_JTAGTransistions[ 25 ] = { + { RESET, RESET, 0xFC, 6 }, /* Transitions from RESET */ + { RESET, IDLE, 0x00, 1 }, + { RESET, DRPAUSE, 0x50, 5 }, + { RESET, IRPAUSE, 0x68, 6 }, + { IDLE, RESET, 0xE0, 3 }, /* Transitions from IDLE */ + { IDLE, DRPAUSE, 0xA0, 4 }, + { IDLE, IRPAUSE, 0xD0, 5 }, + { DRPAUSE, RESET, 0xF8, 5 }, /* Transitions from DRPAUSE */ + { DRPAUSE, IDLE, 0xC0, 3 }, + { DRPAUSE, IRPAUSE, 0xF4, 7 }, + { DRPAUSE, DRPAUSE, 0xE8, 6 }, /* 06/14/06 Support POLING STATUS LOOP*/ + { IRPAUSE, RESET, 0xF8, 5 }, /* Transitions from IRPAUSE */ + { IRPAUSE, IDLE, 0xC0, 3 }, + { IRPAUSE, DRPAUSE, 0xE8, 6 }, + { DRPAUSE, SHIFTDR, 0x80, 2 }, /* Extra transitions using SHIFTDR */ + { IRPAUSE, SHIFTDR, 0xE0, 5 }, + { SHIFTDR, DRPAUSE, 0x80, 2 }, + { SHIFTDR, IDLE, 0xC0, 3 }, + { IRPAUSE, SHIFTIR, 0x80, 2 }, /* Extra transitions using SHIFTIR */ + { SHIFTIR, IRPAUSE, 0x80, 2 }, + { SHIFTIR, IDLE, 0xC0, 3 }, + { DRPAUSE, DRCAPTURE, 0xE0, 4 }, /* 11/15/05 Support DRCAPTURE*/ + { DRCAPTURE, DRPAUSE, 0x80, 2 }, + { IDLE, DRCAPTURE, 0x80, 2 }, + { IRPAUSE, DRCAPTURE, 0xE0, 4 } +}; + +/*************************************************************** + * + * List to hold all LVDS pairs. + * + ***************************************************************/ + +LVDSPair * g_pLVDSList = NULL; +unsigned short g_usLVDSPairCount = 0; + +/*************************************************************** + * + * Function prototypes. + * + ***************************************************************/ + +signed char ispVMCode(); +signed char ispVMDataCode(); +long int ispVMDataSize(); +void ispVMData( unsigned char * Data ); +signed char ispVMShift( signed char Code ); +signed char ispVMAmble( signed char Code ); +signed char ispVMLoop( unsigned short a_usLoopCount ); +signed char ispVMBitShift( signed char mode, unsigned short bits ); +void ispVMComment( unsigned short a_usCommentSize ); +void ispVMHeader( unsigned short a_usHeaderSize ); +signed char ispVMLCOUNT( unsigned short a_usCountSize ); +void ispVMClocks( unsigned short Clocks ); +void ispVMBypass( signed char ScanType, unsigned short Bits ); +void ispVMStateMachine( signed char NextState ); +void ispVMStart(); +void ispVMEnd(); +signed char ispVMSend(unsigned short int); +signed char ispVMRead(unsigned short int); +signed char ispVMReadandSave(unsigned short int); +signed char ispVMProcessLVDS( unsigned short a_usLVDSCount ); + + +/*************************************************************** + * + * External variables and functions in ispvm_ui.c module + * + ***************************************************************/ +extern void vme_out_char(unsigned char charOut); +extern void vme_out_hex(unsigned char hexOut); +extern void vme_out_string(char *stringOut); +extern unsigned char GetByte(); +extern void ispVMMemManager( signed char types, unsigned short size ); + +/*************************************************************** + * + * External variables and functions in hardware.c module + * + ***************************************************************/ +extern void ispVMDelay( unsigned short int a_usMicroSecondDelay ); +extern unsigned char readPort(); +extern void writePort( unsigned long pins, unsigned char value ); +extern void sclock(); +extern signed char g_cCurrentJTAGState; +extern const unsigned long g_ucPinTDI; +extern const unsigned long g_ucPinTCK; +extern const unsigned long g_ucPinTMS; +extern const unsigned long g_ucPinENABLE; +extern const unsigned long g_ucPinTRST; +extern const unsigned long g_ucPinTDO; + +#ifdef VME_DEBUG + +/*************************************************************** + * + * GetState + * + * Returns the state as a string based on the opcode. Only used + * for debugging purposes. + * + ***************************************************************/ + +const char * GetState( unsigned char a_ucState ) +{ + switch( a_ucState ) { + case RESET: + return( "RESET" ); + case IDLE: + return( "IDLE" ); + case IRPAUSE: + return( "IRPAUSE" ); + case DRPAUSE: + return( "DRPAUSE" ); + case SHIFTIR: + return( "SHIFTIR" ); + case SHIFTDR: + return( "SHIFTDR" ); + case DRCAPTURE:/* 11/15/05 support DRCAPTURE*/ + return( "DRCAPTURE" ); + default: + break; + } + + return 0; +} + +/*************************************************************** + * + * PrintData + * + * Prints the data. Only used for debugging purposes. + * + ***************************************************************/ + +void PrintData( unsigned short a_iDataSize, unsigned char * a_pucData ) +{ + //09/11/07 NN added local variables initialization + unsigned short usByteSize = 0; + unsigned short usBitIndex = 0; + signed short usByteIndex = 0; + unsigned char ucByte = 0; + unsigned char ucFlipByte = 0; + + if ( a_iDataSize % 8 ) { + //09/11/07 NN Type cast mismatch variables + usByteSize = (unsigned short)(a_iDataSize / 8 + 1); + } + else { + //09/11/07 NN Type cast mismatch variables + usByteSize = (unsigned short)(a_iDataSize / 8); + } + printf( "(" ); + //09/11/07 NN Type cast mismatch variables + for ( usByteIndex = (signed short)(usByteSize - 1); usByteIndex >= 0; usByteIndex-- ) { + ucByte = a_pucData[ usByteIndex ]; + ucFlipByte = 0x00; + + /*************************************************************** + * + * Flip each byte. + * + ***************************************************************/ + + for ( usBitIndex = 0; usBitIndex < 8; usBitIndex++ ) { + ucFlipByte <<= 1; + if ( ucByte & 0x1) { + ucFlipByte |= 0x1; + } + + ucByte >>= 1; + } + + /*************************************************************** + * + * Print the flipped byte. + * + ***************************************************************/ + + printf( "%.02X", ucFlipByte ); + if ( ( usByteSize - usByteIndex ) % 40 == 39 ) { + printf( "\n\t\t" ); + } + if(usByteIndex < 0) + break; + } + printf( ")" ); +} +#endif //VME_DEBUG + +/*************************************************************** + * + * ispVMDataSize + * + * Returns a VME-encoded number, usually used to indicate the + * bit length of an SIR/SDR command. + * + ***************************************************************/ + +long int ispVMDataSize() +{ + //09/11/07 NN added local variables initialization + long int iSize = 0; + signed char cCurrentByte = 0; + signed char cIndex = 0; + cIndex = 0; + while ( ( cCurrentByte = GetByte() ) & 0x80 ) { + iSize |= ( ( long int ) ( cCurrentByte & 0x7F ) ) << cIndex; + cIndex += 7; + } + iSize |= ( ( long int ) ( cCurrentByte & 0x7F ) ) << cIndex; + return iSize; +} + +/*************************************************************** + * + * ispVMCode + * + * This is the heart of the embedded engine. All the high-level opcodes + * are extracted here. Once they have been identified, then it + * will call other functions to handle the processing. + * + ***************************************************************/ + +signed char ispVMCode() +{ + //09/11/07 NN added local variables initialization + unsigned short iRepeatSize = 0; + signed char cOpcode = 0; + signed char cRetCode = 0; + unsigned char ucState = 0; + unsigned short usDelay = 0; + unsigned short usToggle = 0; + unsigned char usByte = 0; + + /*************************************************************** + * + * Check the compression flag only if this is the first time + * this function is entered. Do not check the compression flag if + * it is being called recursively from other functions within + * the embedded engine. + * + ***************************************************************/ + + if ( !( g_usDataType & LHEAP_IN ) && !( g_usDataType & HEAP_IN ) ) { + usByte = GetByte(); + if ( usByte == 0xf1 ) { + g_usDataType |= COMPRESS; + } + else if ( usByte == 0xf2 ) { + g_usDataType &= ~COMPRESS; + } + else { + return VME_INVALID_FILE; + } + } + + /*************************************************************** + * + * Begin looping through all the VME opcodes. + * + ***************************************************************/ + + while ( ( cOpcode = GetByte() ) >= 0 ) { + + switch ( cOpcode ) { + case STATE: + + /*************************************************************** + * + * Step the JTAG state machine. + * + ***************************************************************/ + + ucState = GetByte(); + + /*************************************************************** + * + * Step the JTAG state machine to DRCAPTURE to support Looping. + * + ***************************************************************/ + + if ( (g_usDataType & LHEAP_IN) && + (ucState == DRPAUSE ) && + ( g_cCurrentJTAGState == ucState )) + { + ispVMStateMachine( DRCAPTURE ); + } + + ispVMStateMachine( ucState ); + +#ifdef VME_DEBUG + if ( g_usDataType & LHEAP_IN ) { + printf( "LDELAY %s ", GetState( ucState ) ); + } + else { + printf( "STATE %s;\n", GetState( ucState ) ); + } +#endif //VME_DEBUG + break; + case SIR: + case SDR: + case XSDR: + +#ifdef VME_DEBUG + switch( cOpcode ) { + case SIR: + printf( "SIR " ); + break; + case SDR: + case XSDR: + if ( g_usDataType & LHEAP_IN ) { + printf( "LSDR " ); + } + else { + printf( "SDR " ); + } + break; + } +#endif //VME_DEBUG + /*************************************************************** + * + * Shift in data into the device. + * + ***************************************************************/ + + cRetCode = ispVMShift( cOpcode ); + if ( cRetCode != 0 ) { + return ( cRetCode ); + } + break; + case WAIT: + + /*************************************************************** + * + * Observe delay. + * + ***************************************************************/ + + //09/11/07 NN Type cast mismatch variables + usDelay = (unsigned short) ispVMDataSize(); + ispVMDelay( usDelay ); + +#ifdef VME_DEBUG + if ( usDelay & 0x8000 ) { + + /*************************************************************** + * + * Since MSB is set, the delay time must be decoded to + * millisecond. The SVF2VME encodes the MSB to represent + * millisecond. + * + ***************************************************************/ + + usDelay &= ~0x8000; + if ( g_usDataType & LHEAP_IN ) { + printf( "%.2E SEC;\n", ( float ) usDelay / 1000 ); + } + else { + printf( "RUNTEST %.2E SEC;\n", ( float ) usDelay / 1000 ); + } + } + else { + + /*************************************************************** + * + * Since MSB is not set, the delay time is given as microseconds. + * + ***************************************************************/ + + if ( g_usDataType & LHEAP_IN ) { + printf( "%.2E SEC;\n", ( float ) usDelay / 1000000 ); + } + else { + printf( "RUNTEST %.2E SEC;\n", ( float ) usDelay / 1000000 ); + } + } +#endif //VME_DEBUG + break; + case TCK: + + /*************************************************************** + * + * Issue clock toggles. + * + ***************************************************************/ + + //09/11/07 NN Type cast mismatch variables + usToggle = (unsigned short) ispVMDataSize(); + ispVMClocks( usToggle ); + +#ifdef VME_DEBUG + printf( "RUNTEST %d TCK;\n", usToggle ); +#endif //VME_DEBUG + break; + case ENDDR: + + /*************************************************************** + * + * Set the ENDDR. + * + ***************************************************************/ + + g_ucEndDR = GetByte(); + +#ifdef VME_DEBUG + printf( "ENDDR %s;\n", GetState( g_ucEndDR ) ); +#endif //VME_DEBUG + break; + case ENDIR: + + /*************************************************************** + * + * Set the ENDIR. + * + ***************************************************************/ + + g_ucEndIR = GetByte(); + +#ifdef VME_DEBUG + printf( "ENDIR %s;\n", GetState( g_ucEndIR ) ); +#endif //VME_DEBUG + break; + case HIR: + case TIR: + case HDR: + case TDR: + +#ifdef VME_DEBUG + switch( cOpcode ) { + case HIR: + printf( "HIR " ); + break; + case TIR: + printf( "TIR " ); + break; + case HDR: + printf( "HDR " ); + break; + case TDR: + printf( "TDR " ); + break; + } +#endif //VME_DEBUG + + /*************************************************************** + * + * Set the header/trailer of the device in order to bypass + * successfully. + * + ***************************************************************/ + + cRetCode = ispVMAmble( cOpcode ); + if ( cRetCode != 0 ) { + return ( cRetCode ); + } + +#ifdef VME_DEBUG + printf( ";\n" ); +#endif //VME_DEBUG + break; + case MEM: + + /*************************************************************** + * + * The maximum RAM required to support processing one row of the + * VME file. + * + ***************************************************************/ + + //09/11/07 NN Type cast mismatch variables + g_usMaxSize = (unsigned short) ispVMDataSize(); + +#ifdef VME_DEBUG + printf( "// MEMSIZE %d\n", g_usMaxSize ); +#endif //VME_DEBUG + break; + case VENDOR: + + /*************************************************************** + * + * Set the VENDOR type. + * + ***************************************************************/ + + cOpcode = GetByte(); + switch ( cOpcode ) { + case LATTICE: +#ifdef VME_DEBUG + printf( "// VENDOR LATTICE\n" ); +#endif //VME_DEBUG + g_cVendor = LATTICE; + break; + case ALTERA: +#ifdef VME_DEBUG + printf( "// VENDOR ALTERA\n" ); +#endif //VME_DEBUG + g_cVendor = ALTERA; + break; + case XILINX: +#ifdef VME_DEBUG + printf( "// VENDOR XILINX\n" ); +#endif //VME_DEBUG + g_cVendor = XILINX; + break; + default: + break; + } + break; + case SETFLOW: + + /*************************************************************** + * + * Set the flow control. Flow control determines the personality + * of the embedded engine. + * + ***************************************************************/ + + //09/11/07 NN Type cast mismatch variables + g_usFlowControl |= (unsigned short) ispVMDataSize(); + break; + case RESETFLOW: + + /*************************************************************** + * + * Unset the flow control. + * + ***************************************************************/ + + //09/11/07 NN Type cast mismatch variables + g_usFlowControl &= (unsigned short) ~( ispVMDataSize() ); + break; + case HEAP: + + /*************************************************************** + * + * Allocate heap size to store loops. + * + ***************************************************************/ + + cRetCode = GetByte(); + if ( cRetCode != SECUREHEAP ) { + return VME_INVALID_FILE; + } + //09/11/07 NN Type cast mismatch variables + g_iHEAPSize = (unsigned short) ispVMDataSize(); + + /**************************************************************************** + * + * Store the maximum size of the HEAP buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if ( g_iHEAPSize > g_usHeapSize ) { + g_usHeapSize = g_iHEAPSize; + } + + ispVMMemManager( HEAP, ( unsigned short ) g_iHEAPSize ); + break; + case REPEAT: + + /*************************************************************** + * + * Execute loops. + * + ***************************************************************/ + + g_usRepeatLoops = 0; + + //09/11/07 NN Type cast mismatch variables + iRepeatSize = (unsigned short) ispVMDataSize(); + + cRetCode = ispVMLoop( ( unsigned short ) iRepeatSize ); + if ( cRetCode != 0 ) { + return ( cRetCode ); + } + break; + case ENDLOOP: + + /*************************************************************** + * + * Exit point from processing loops. + * + ***************************************************************/ + + return ( cRetCode ); + case ENDVME: + + /*************************************************************** + * + * The only valid exit point that indicates end of programming. + * + ***************************************************************/ + + return ( cRetCode ); + case SHR: + + /*************************************************************** + * + * Right-shift address. + * + ***************************************************************/ + + g_usFlowControl |= SHIFTRIGHT; + + //09/11/07 NN Type cast mismatch variables + g_usShiftValue = (unsigned short) (g_usRepeatLoops * (unsigned short)GetByte()); + break; + case SHL: + + /*************************************************************** + * + * Left-shift address. + * + ***************************************************************/ + + g_usFlowControl |= SHIFTLEFT; + + //09/11/07 NN Type cast mismatch variables + g_usShiftValue = (unsigned short)(g_usRepeatLoops * (unsigned short)GetByte()); + break; + case FREQUENCY: + + /*************************************************************** + * + * Set the frequency. + * + ***************************************************************/ + + //09/11/07 NN Type cast mismatch variables + g_iFrequency = (int) (ispVMDataSize() ); + //10/23/08 NN changed to check if the frequency smaller than 1000 + if(g_iFrequency >= 1000) + { + g_iFrequency = g_iFrequency / 1000; + if(g_iFrequency == 1) + g_iFrequency = 1000; +#ifdef VME_DEBUG + printf( "FREQUENCY %.2E HZ;\n", ( float ) g_iFrequency * 1000 ); +#endif //VME_DEBUG + } + else + { + if(g_iFrequency == 0) + g_iFrequency = 1000; +#ifdef VME_DEBUG + printf( "FREQUENCY %.2E HZ;\n", ( float ) g_iFrequency ); +#endif //VME_DEBUG + } + break; + case LCOUNT: + + /*************************************************************** + * + * Process LCOUNT command. + * + ***************************************************************/ + + cRetCode = ispVMLCOUNT( ( unsigned short ) ispVMDataSize() ); + if ( cRetCode != 0 ) { + return ( cRetCode ); + } + break; + case VUES: + + /*************************************************************** + * + * Set the flow control to verify USERCODE. + * + ***************************************************************/ + + g_usFlowControl |= VERIFYUES; + break; + case COMMENT: + + /*************************************************************** + * + * Display comment. + * + ***************************************************************/ + + ispVMComment( ( unsigned short ) ispVMDataSize() ); + break; + case LVDS: + + /*************************************************************** + * + * Process LVDS command. + * + ***************************************************************/ + + ispVMProcessLVDS( ( unsigned short ) ispVMDataSize() ); + break; + case HEADER: + + /*************************************************************** + * + * Discard header. + * + ***************************************************************/ + + ispVMHeader( ( unsigned short ) ispVMDataSize() ); + break; + /* 03/14/06 Support Toggle ispENABLE signal*/ + case ispEN: + ucState = GetByte(); + if((ucState == ON)||(ucState == 0x01)) + writePort( g_ucPinENABLE, 0x01 ); + else + writePort( g_ucPinENABLE, 0x00 ); + ispVMDelay( 1 ); + break; + /* 05/24/06 support Toggle TRST pin*/ + case TRST: + ucState = GetByte(); + if(ucState == 0x01) + writePort( g_ucPinTRST, 0x01 ); + else + writePort( g_ucPinTRST, 0x00 ); + ispVMDelay( 1 ); + break; + default: + + /*************************************************************** + * + * Invalid opcode encountered. + * + ***************************************************************/ + +#ifdef VME_DEBUG + printf( "\nINVALID OPCODE: 0x%.2X\n", cOpcode ); +#endif //VME_DEBUG + + return VME_INVALID_FILE; + } + } + + /*************************************************************** + * + * Invalid exit point. Processing the token 'ENDVME' is the only + * valid way to exit the embedded engine. + * + ***************************************************************/ + + return ( VME_INVALID_FILE ); +} + +/*************************************************************** + * + * ispVMDataCode + * + * Processes the TDI/TDO/MASK/DMASK etc of an SIR/SDR command. + * + ***************************************************************/ + +signed char ispVMDataCode() +{ + //09/11/07 NN added local variables initialization + signed char cDataByte = 0; + signed char siDataSource = 0; /*source of data from file by default*/ + + if ( g_usDataType & HEAP_IN ) { + siDataSource = 1; /*the source of data from memory*/ + } + + /**************************************************************************** + * + * Clear the data type register. + * + *****************************************************************************/ + + g_usDataType &= ~( MASK_DATA + TDI_DATA + TDO_DATA + DMASK_DATA + CMASK_DATA ); + + /**************************************************************************** + * + * Iterate through SIR/SDR command and look for TDI, TDO, MASK, etc. + * + *****************************************************************************/ + + while ( ( cDataByte = GetByte() ) >= 0 ) { + + ispVMMemManager( cDataByte, g_usMaxSize ); + switch ( cDataByte ) { + case TDI: + + /**************************************************************************** + * + * Store the maximum size of the TDI buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if ( g_usiDataSize > g_usTDISize ) { + g_usTDISize = g_usiDataSize; + } + /**************************************************************************** + * + * Updated data type register to indicate that TDI data is currently being + * used. Process the data in the VME file into the TDI buffer. + * + *****************************************************************************/ + + g_usDataType |= TDI_DATA; + ispVMData( g_pucInData ); + break; + case XTDO: + + /**************************************************************************** + * + * Store the maximum size of the TDO buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if ( g_usiDataSize > g_usTDOSize ) { + g_usTDOSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Updated data type register to indicate that TDO data is currently being + * used. + * + *****************************************************************************/ + + g_usDataType |= TDO_DATA; + break; + case TDO: + + /**************************************************************************** + * + * Store the maximum size of the TDO buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if ( g_usiDataSize > g_usTDOSize ) { + g_usTDOSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Updated data type register to indicate that TDO data is currently being + * used. Process the data in the VME file into the TDO buffer. + * + *****************************************************************************/ + + g_usDataType |= TDO_DATA; + ispVMData( g_pucOutData ); + break; + case MASK: + + /**************************************************************************** + * + * Store the maximum size of the MASK buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if ( g_usiDataSize > g_usMASKSize ) { + g_usMASKSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Updated data type register to indicate that MASK data is currently being + * used. Process the data in the VME file into the MASK buffer. + * + *****************************************************************************/ + + g_usDataType |= MASK_DATA; + ispVMData( g_pucOutMaskData ); + break; + case DMASK: + + /**************************************************************************** + * + * Store the maximum size of the DMASK buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if ( g_usiDataSize > g_usDMASKSize ) { + g_usDMASKSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Updated data type register to indicate that DMASK data is currently being + * used. Process the data in the VME file into the DMASK buffer. + * + *****************************************************************************/ + + g_usDataType |= DMASK_DATA; + ispVMData( g_pucOutDMaskData ); + break; + case CMASK: + + /**************************************************************************** + * + * Updated data type register to indicate that CMASK data is currently being + * used. Process the data in the VME file into the CMASK buffer. + * + *****************************************************************************/ + + g_usDataType |= CMASK_DATA; + ispVMData( g_pucOutMaskData ); + break; + case CONTINUE: + return ( 0 ); + default: + + /**************************************************************************** + * + * Encountered invalid opcode. + * + *****************************************************************************/ + + return ( VME_INVALID_FILE ); + } + + switch ( cDataByte ) { + case TDI: + + /**************************************************************************** + * + * Left bit shift. Used when performing algorithm looping. + * + *****************************************************************************/ + + if ( g_usFlowControl & SHIFTLEFT ) { + ispVMBitShift( SHL, g_usShiftValue ); + g_usFlowControl &= ~SHIFTLEFT; + } + + /**************************************************************************** + * + * Right bit shift. Used when performing algorithm looping. + * + *****************************************************************************/ + + if ( g_usFlowControl & SHIFTRIGHT ) { + ispVMBitShift( SHR, g_usShiftValue ); + g_usFlowControl &= ~SHIFTRIGHT; + } + default: + break; + } + + if ( siDataSource ) { + g_usDataType |= HEAP_IN; /*restore data from memory*/ + } + } + + if ( siDataSource ) { /*fetch data from heap memory upon return*/ + g_usDataType |= HEAP_IN; + } + + if ( cDataByte < 0 ) { + + /**************************************************************************** + * + * Encountered invalid opcode. + * + *****************************************************************************/ + + return ( VME_INVALID_FILE ); + } + else { + return ( 0 ); + } +} + +/*************************************************************** + * + * ispVMData + * Extract one row of data operand from the current data type opcode. Perform + * the decompression if necessary. Extra RAM is not required for the + * decompression process. The decompression scheme employed in this module + * is on row by row basis. The format of the data stream: + * [compression code][compressed data stream] + * 0x00 --No compression + * 0x01 --Compress by 0x00. + * Example: + * Original stream: 0x000000000000000000000001 + * Compressed stream: 0x01000901 + * Detail: 0x01 is the code, 0x00 is the key, + * 0x09 is the count of 0x00 bytes, + * 0x01 is the uncompressed byte. + * 0x02 --Compress by 0xFF. + * Example: + * Original stream: 0xFFFFFFFFFFFFFFFFFFFFFF01 + * Compressed stream: 0x02FF0901 + * Detail: 0x02 is the code, 0xFF is the key, + * 0x09 is the count of 0xFF bytes, + * 0x01 is the uncompressed byte. + * 0x03 + * : : + * 0xFE -- Compress by nibble blocks. + * Example: + * Original stream: 0x84210842108421084210 + * Compressed stream: 0x0584210 + * Detail: 0x05 is the code, means 5 nibbles block. + * 0x84210 is the 5 nibble blocks. + * The whole row is 80 bits given by g_usiDataSize. + * The number of times the block repeat itself + * is found by g_usiDataSize/(4*0x05) which is 4. + * 0xFF -- Compress by the most frequently happen byte. + * Example: + * Original stream: 0x04020401030904040404 + * Compressed stream: 0xFF04(0,1,0x02,0,1,0x01,1,0x03,1,0x09,0,0,0) + * or: 0xFF044090181C240 + * Detail: 0xFF is the code, 0x04 is the key. + * a bit of 0 represent the key shall be put into + * the current bit position and a bit of 1 + * represent copying the next of 8 bits of data + * in. + * + ***************************************************************/ + +void ispVMData( unsigned char * ByteData ) +{ + //09/11/07 NN added local variables initialization + unsigned short size = 0; + unsigned short i, j, m, getData = 0; + unsigned char cDataByte = 0; + unsigned char compress = 0; + unsigned short FFcount = 0; + unsigned char compr_char = 0xFF; + unsigned short index = 0; + signed char compression = 0; + + /*convert number in bits to bytes*/ + if (g_usiDataSize%8>0) { + //09/11/07 NN Type cast mismatch variables + size = (unsigned short)(g_usiDataSize/8 + 1); + } + else { + //09/11/07 NN Type cast mismatch variables + size = (unsigned short)(g_usiDataSize/8); + } + + /* If there is compression, then check if compress by key of 0x00 or 0xFF + or by other keys or by nibble blocks*/ + + if ( g_usDataType & COMPRESS ) { + compression = 1; + if ( ( ( compress = GetByte() ) == VAR ) && ( g_usDataType & HEAP_IN ) ) { + getData = 1; + g_usDataType &= ~(HEAP_IN); + compress = GetByte(); + } + + switch (compress){ + case 0x00: + /* No compression */ + compression = 0; + break; + case 0x01: + /* Compress by byte 0x00 */ + compr_char = 0x00; + break; + case 0x02: + /* Compress by byte 0xFF */ + compr_char = 0xFF; + break; + case 0xFF: + /* Huffman encoding */ + compr_char = GetByte(); + i = 8; + for ( index = 0; index < size; index++ ) { + ByteData[ index ] = 0x00; + if ( i > 7 ) { + cDataByte = GetByte(); + i = 0; + } + if ((cDataByte << i++) & 0x80) + m = 8; + else { + ByteData[index] = compr_char; + m = 0; + } + + for (j = 0; j < m; j++) { + if (i > 7) { + cDataByte = GetByte(); + i = 0; + } + ByteData[index] |=((cDataByte << i++)&0x80) >> j; + } + } + size = 0; + break; + default: + for (index = 0; index < size; index++) + ByteData[index] = 0x00; + for (index = 0; index < compress; index++) { + if (index%2 == 0) + cDataByte = GetByte(); + for (i = 0; i < size*2/compress; i++){ + //09/11/07 NN Type cast mismatch variables + j = (unsigned short)(index + (i*(unsigned short)compress)); + /*clear the nibble to zero first*/ + if (j%2) { + if (index%2) + ByteData[j/2] |= cDataByte & 0x0F; + else + ByteData[j/2] |= cDataByte >> 4; + } + else { + if (index%2) + ByteData[j/2] |= cDataByte << 4; + else + ByteData[j/2] |= cDataByte & 0xF0; + } + } + } + size = 0; + break; + } + } + + FFcount = 0; + + /* Decompress by byte 0x00 or 0xFF */ + for (index = 0; index < size; index++) { + if (FFcount <= 0) { + cDataByte = GetByte(); + if ((cDataByte == VAR) && (g_usDataType&HEAP_IN) && !getData && !(g_usDataType&COMPRESS)) { + getData = 1; + g_usDataType &= ~(HEAP_IN); + cDataByte = GetByte(); + } + ByteData[index] = cDataByte; + if ((compression) &&(cDataByte == compr_char)) /*decompression is on*/ + //09/11/07 NN Type cast mismatch variables + FFcount = (unsigned short) ispVMDataSize(); /*The number of 0xFF or 0x00 bytes*/ + } + else { + FFcount--; /*Use up the 0xFF chain first*/ + ByteData[index] = compr_char; + } + } + + if (getData) { + g_usDataType |= HEAP_IN; + getData = 0; + } +} + +/*************************************************************** + * + * ispVMShift + * + * Processes the SDR/XSDR/SIR commands. + * + ***************************************************************/ + +signed char ispVMShift( signed char a_cCode ) +{ + //09/11/07 NN added local variables initialization + unsigned short iDataIndex = 0; + unsigned short iReadLoop = 0; + signed char cRetCode = 0; + + cRetCode=0; + //09/11/07 NN Type cast mismatch variables + g_usiDataSize = (unsigned short) ispVMDataSize(); + + g_usDataType &= ~( SIR_DATA + EXPRESS + SDR_DATA ); /*clear the flags first*/ + switch ( a_cCode ) { + case SIR: + g_usDataType |= SIR_DATA; + /* 1/15/04 If performing cascading, then go directly to SHIFTIR. Else, + go to IRPAUSE before going to SHIFTIR */ + if ( g_usFlowControl & CASCADE ) { + ispVMStateMachine( SHIFTIR ); + } + else { + ispVMStateMachine( IRPAUSE ); + ispVMStateMachine( SHIFTIR ); + if ( g_usHeadIR > 0 ){ + ispVMBypass( HIR, g_usHeadIR ); + sclock(); + } + } + break; + case XSDR: + g_usDataType |= EXPRESS; /*mark simultaneous in and out*/ + /* FALLTHRU */ + case SDR: + g_usDataType |= SDR_DATA; + /* 1/15/04 If already in SHIFTDR, then do not move state or shift in header. + This would imply that the previously shifted frame was a cascaded frame. */ + if ( g_cCurrentJTAGState != SHIFTDR ) { + /* 1/15/04 If performing cascading, then go directly to SHIFTDR. Else, + go to DRPAUSE before going to SHIFTDR */ + if ( g_usFlowControl & CASCADE ) { + if ( g_cCurrentJTAGState == DRPAUSE ) { + ispVMStateMachine( SHIFTDR ); + /* 1/15/04 If cascade flag has been set and the current state is + DRPAUSE, this implies that the first cascaded frame is about to + be shifted in. The header must be shifted prior to shifting + the first cascaded frame. */ + if ( g_usHeadDR > 0 ) { + ispVMBypass( HDR, g_usHeadDR ); + sclock(); + } + } + else { + ispVMStateMachine( SHIFTDR ); + } + } + else { + ispVMStateMachine( DRPAUSE ); + ispVMStateMachine( SHIFTDR ); + if ( g_usHeadDR > 0 ) { + ispVMBypass( HDR, g_usHeadDR ); + sclock(); + } + } + } + break; + default: + return ( VME_INVALID_FILE ); + } + + cRetCode = ispVMDataCode(); + + if ( cRetCode != 0 ) { + return ( VME_INVALID_FILE ); + } + +#ifdef VME_DEBUG + printf( "%d ", g_usiDataSize ); + + if ( g_usDataType & TDI_DATA ) { + printf( "TDI " ); + PrintData( g_usiDataSize, g_pucInData ); + } + + if ( g_usDataType & TDO_DATA ) { + printf( "\n\t\tTDO " ); + PrintData( g_usiDataSize, g_pucOutData ); + } + + if ( g_usDataType & MASK_DATA ) { + printf( "\n\t\tMASK " ); + PrintData( g_usiDataSize, g_pucOutMaskData ); + } + + if ( g_usDataType & DMASK_DATA ) { + printf( "\n\t\tDMASK " ); + PrintData( g_usiDataSize, g_pucOutDMaskData ); + } + + printf( ";\n" ); +#endif //VME_DEBUG + + if ( g_usDataType & TDO_DATA || g_usDataType & DMASK_DATA ) { + if(g_usDataType & DMASK_DATA){ + cRetCode = ispVMReadandSave( g_usiDataSize ); + if(!cRetCode){ + if ( g_usTailDR > 0 ) { + sclock(); + ispVMBypass( TDR, g_usTailDR ); + } + ispVMStateMachine( DRPAUSE ); + ispVMStateMachine( SHIFTDR ); + if( g_usHeadDR > 0 ){ + ispVMBypass( HDR, g_usHeadDR ); + sclock(); + } + for ( iDataIndex=0; iDataIndex < g_usiDataSize / 8 + 1; iDataIndex++ ) + g_pucInData[ iDataIndex ] = g_pucOutData[ iDataIndex ]; + g_usDataType &= ~( TDO_DATA+ DMASK_DATA ); + cRetCode = ispVMSend( g_usiDataSize ); + } + } + else{ + cRetCode = ispVMRead( g_usiDataSize ); + if ( cRetCode == -1 && g_cVendor == XILINX ) { + for( iReadLoop = 0; iReadLoop < 30; iReadLoop++ ){ + cRetCode = ispVMRead( g_usiDataSize ); + if( !cRetCode ) { + break; + } + else { + ispVMStateMachine( DRPAUSE ); /*Always DRPAUSE*/ + /*Bypass other devices when appropriate*/ + ispVMBypass( TDR, g_usTailDR ); + ispVMStateMachine( g_ucEndDR ); + ispVMStateMachine( IDLE ); + ispVMDelay( 0x8001 ); + } + } + } + } + } + else { /*TDI only*/ + cRetCode = ispVMSend( g_usiDataSize ); + } + + /*transfer the input data to the output buffer for the next verify*/ + if ( ( g_usDataType & EXPRESS ) || ( a_cCode == SDR ) ) { + if ( g_pucOutData ) { + for ( iDataIndex=0; iDataIndex < g_usiDataSize / 8 + 1; iDataIndex++ ) + g_pucOutData[ iDataIndex ] = g_pucInData[ iDataIndex ]; + } + } + + switch( a_cCode ) { + case SIR: + /* 1/15/04 If not performing cascading, then shift ENDIR */ + if ( !( g_usFlowControl & CASCADE ) ) { + if ( g_usTailIR > 0 ) { + sclock(); + ispVMBypass( TIR, g_usTailIR ); + } + ispVMStateMachine( g_ucEndIR ); + } + break; + case XSDR: + case SDR: + /* 1/15/04 If not performing cascading, then shift ENDDR */ + if ( !( g_usFlowControl & CASCADE ) ) { + if ( g_usTailDR > 0 ) { + sclock(); + ispVMBypass( TDR, g_usTailDR ); + } + ispVMStateMachine( g_ucEndDR ); + } + break; + default: + break; + } + + return ( cRetCode ); +} + +/*************************************************************** + * + * ispVMAmble + * + * This routine is to extract Header and Trailer parameter for SIR and + * SDR operations. + * + * The Header and Trailer parameter are the pre-amble and post-amble bit + * stream need to be shifted into TDI or out of TDO of the devices. Mostly + * is for the purpose of bypassing the leading or trailing devices. ispVM + * supports only shifting data into TDI to bypass the devices. + * + * For a single device, the header and trailer parameters are all set to 0 + * as default by ispVM. If it is for multiple devices, the header and trailer + * value will change as specified by the VME file. + * + ***************************************************************/ + +signed char ispVMAmble( signed char Code ) +{ + signed char compress = 0; + //09/11/07 NN Type cast mismatch variables + g_usiDataSize = (unsigned short)ispVMDataSize(); + +#ifdef VME_DEBUG + printf( "%d", g_usiDataSize ); +#endif //VME_DEBUG + + if ( g_usiDataSize ) { + + /**************************************************************************** + * + * Discard the TDI byte and set the compression bit in the data type register + * to false if compression is set because TDI data after HIR/HDR/TIR/TDR is not + * compressed. + * + *****************************************************************************/ + + GetByte(); + if ( g_usDataType & COMPRESS ) { + g_usDataType &= ~( COMPRESS ); + compress = 1; + } + } + + switch ( Code ) { + case HIR: + + /**************************************************************************** + * + * Store the maximum size of the HIR buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if ( g_usiDataSize > g_usHIRSize ) { + g_usHIRSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Assign the HIR value and allocate memory. + * + *****************************************************************************/ + + g_usHeadIR = g_usiDataSize; + if ( g_usHeadIR ) { + ispVMMemManager( HIR, g_usHeadIR ); + ispVMData( g_pucHIRData ); + +#ifdef VME_DEBUG + printf( " TDI " ); + PrintData( g_usHeadIR, g_pucHIRData ); +#endif //VME_DEBUG + } + break; + case TIR: + + /**************************************************************************** + * + * Store the maximum size of the TIR buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if ( g_usiDataSize > g_usTIRSize ) { + g_usTIRSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Assign the TIR value and allocate memory. + * + *****************************************************************************/ + + g_usTailIR = g_usiDataSize; + if ( g_usTailIR ) { + ispVMMemManager( TIR, g_usTailIR ); + ispVMData( g_pucTIRData ); + +#ifdef VME_DEBUG + printf( " TDI " ); + PrintData( g_usTailIR, g_pucTIRData ); +#endif //VME_DEBUG + } + break; + case HDR: + + /**************************************************************************** + * + * Store the maximum size of the HDR buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if ( g_usiDataSize > g_usHDRSize ) { + g_usHDRSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Assign the HDR value and allocate memory. + * + *****************************************************************************/ + + g_usHeadDR = g_usiDataSize; + if ( g_usHeadDR ) { + ispVMMemManager( HDR, g_usHeadDR ); + ispVMData( g_pucHDRData ); + +#ifdef VME_DEBUG + printf( " TDI " ); + PrintData( g_usHeadDR, g_pucHDRData ); +#endif //VME_DEBUG + } + break; + case TDR: + + /**************************************************************************** + * + * Store the maximum size of the TDR buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if ( g_usiDataSize > g_usTDRSize ) { + g_usTDRSize = g_usiDataSize; + } + + /**************************************************************************** + * + * Assign the TDR value and allocate memory. + * + *****************************************************************************/ + + g_usTailDR = g_usiDataSize; + if ( g_usTailDR ) { + ispVMMemManager( TDR, g_usTailDR ); + ispVMData( g_pucTDRData ); + +#ifdef VME_DEBUG + printf( " TDI " ); + PrintData( g_usTailDR, g_pucTDRData ); +#endif //VME_DEBUG + } + break; + default: + break; + } + + /**************************************************************************** + * + * Re-enable compression if it was previously set. + * + *****************************************************************************/ + + if ( compress ) { + g_usDataType |= COMPRESS; + } + + if ( g_usiDataSize ) { + Code = GetByte(); + if ( Code == CONTINUE ) { + return 0; + } + else { + + /**************************************************************************** + * + * Encountered invalid opcode. + * + *****************************************************************************/ + + return VME_INVALID_FILE; + } + } + + return 0; +} + +/*************************************************************** + * + * ispVMLoop + * + * Perform the function call upon by the REPEAT opcode. + * Memory is to be allocated to store the entire loop from REPEAT to ENDLOOP. + * After the loop is stored then execution begin. The REPEATLOOP flag is set + * on the g_usFlowControl register to indicate the repeat loop is in session + * and therefore fetch opcode from the memory instead of from the file. + * + ***************************************************************/ + +signed char ispVMLoop(unsigned short a_usLoopCount) +{ + //09/11/07 NN added local variables initialization + signed char cRetCode = 0; + unsigned short iHeapIndex = 0; + unsigned short iLoopIndex = 0; + + g_usShiftValue = 0; + for ( iHeapIndex = 0; iHeapIndex < g_iHEAPSize; iHeapIndex++ ) { + g_pucHeapMemory[ iHeapIndex ] = GetByte(); + } + + if ( g_pucHeapMemory[ iHeapIndex - 1 ] != ENDLOOP ) { + return( VME_INVALID_FILE ); + } + + g_usFlowControl |= REPEATLOOP; + g_usDataType |= HEAP_IN; + + for ( iLoopIndex = 0; iLoopIndex < a_usLoopCount; iLoopIndex++ ) { + g_iHeapCounter = 0; + cRetCode = ispVMCode(); + g_usRepeatLoops++; + if ( cRetCode < 0 ) { + break; + } + } + + g_usDataType &= ~( HEAP_IN ); + g_usFlowControl &= ~( REPEATLOOP ); + return ( cRetCode ); +} + +/*************************************************************** + * + * ispVMBitShift + * + * Shift the TDI stream left or right by the number of bits. The data in + * *g_pucInData is of the VME format, so the actual shifting is the reverse of + * IEEE 1532 or SVF format. + * + ***************************************************************/ + +signed char ispVMBitShift(signed char mode, unsigned short bits) +{ + //09/11/07 NN added local variables initialization + unsigned short i = 0; + unsigned short size = 0; + unsigned short tmpbits = 0; + + if (g_usiDataSize%8>0) { + //09/11/07 NN Type cast mismatch variables + size = (unsigned short)(g_usiDataSize/8 + 1); + } + else { + //09/11/07 NN Type cast mismatch variables + size = (unsigned short)(g_usiDataSize/8); + } + + switch(mode) { + case SHR: + for (i = 0; i < size; i++) { + if (g_pucInData[i] != 0) { + tmpbits = bits; + while (tmpbits > 0) { + g_pucInData[i] <<= 1; + if (g_pucInData[i] == 0) { + i--; + g_pucInData[i] = 1; + } + tmpbits--; + } + } + } + break; + case SHL: + for (i = 0; i < size; i++) { + if (g_pucInData[i] != 0) { + tmpbits = bits; + while (tmpbits > 0) { + g_pucInData[i] >>= 1; + if (g_pucInData[i] == 0) { + i--; + g_pucInData[i] = 8; + } + tmpbits--; + } + } + } + break; + default: + return ( VME_INVALID_FILE ); + } + + return (0); +} + +/*************************************************************** + * + * ispVMComment + * + * Displays the SVF comments. + * + ***************************************************************/ + +void ispVMComment( unsigned short a_usCommentSize ) +{ + char cCurByte = 0; + for ( ; a_usCommentSize > 0; a_usCommentSize-- ) { + /**************************************************************************** + * + * Print character to the terminal. + * + *****************************************************************************/ + cCurByte = GetByte(); + vme_out_char( cCurByte ); + } + cCurByte = '\n'; + vme_out_char(cCurByte ); +} + +/*************************************************************** + * + * ispVMHeader + * + * Iterate the length of the header and discard it. + * + ***************************************************************/ + +void ispVMHeader( unsigned short a_usHeaderSize ) +{ + for ( ; a_usHeaderSize > 0; a_usHeaderSize-- ) { + GetByte(); + } +} + +/*************************************************************** + * + * ispVMCalculateCRC32 + * + * Calculate the 32-bit CRC. + * + ***************************************************************/ + +void ispVMCalculateCRC32( unsigned char a_ucData ) +{ + //09/11/07 NN added local variables initialization + unsigned char ucIndex = 0; + unsigned char ucFlipData = 0; + unsigned short usCRCTableEntry = 0; + unsigned int crc_table[ 16 ] = { + 0x0000, 0xCC01, 0xD801, + 0x1400, 0xF001, 0x3C00, + 0x2800, 0xE401, 0xA001, + 0x6C00, 0x7800, 0xB401, + 0x5000, 0x9C01, 0x8801, + 0x4400 + }; + + for ( ucIndex = 0; ucIndex < 8; ucIndex++ ) { + ucFlipData <<= 1; + if ( a_ucData & 0x01 ) { + ucFlipData |= 0x01; + } + a_ucData >>= 1; + } + + //09/11/07 NN Type cast mismatch variables + usCRCTableEntry = (unsigned short)(crc_table[ g_usCalculatedCRC & 0xF ]); + g_usCalculatedCRC = (unsigned short)(( g_usCalculatedCRC >> 4 ) & 0x0FFF); + g_usCalculatedCRC = (unsigned short)(g_usCalculatedCRC ^ usCRCTableEntry ^ crc_table[ ucFlipData & 0xF ]); + usCRCTableEntry = (unsigned short)(crc_table[ g_usCalculatedCRC & 0xF ]); + g_usCalculatedCRC = (unsigned short)(( g_usCalculatedCRC >> 4 ) & 0x0FFF); + g_usCalculatedCRC = (unsigned short)(g_usCalculatedCRC ^ usCRCTableEntry ^ crc_table[ ( ucFlipData >> 4 ) & 0xF ]); +} + +/*************************************************************** + * + * ispVMLCOUNT + * + * Process the intelligent programming loops. + * + ***************************************************************/ + +signed char ispVMLCOUNT( unsigned short a_usCountSize ) +{ + unsigned short usContinue = 1; + unsigned short usIntelBufferIndex = 0; + unsigned short usCountIndex = 0; + signed char cRetCode = 0; + signed char cRepeatHeap = 0; + signed char cOpcode = 0; + unsigned char ucState = 0; + unsigned short usDelay = 0; + unsigned short usToggle = 0; + // unsigned char usByte = 0; + + g_usIntelBufferSize = (unsigned short)ispVMDataSize(); + + /**************************************************************************** + * + * Allocate memory for intel buffer. + * + *****************************************************************************/ + + ispVMMemManager( LHEAP, g_usIntelBufferSize ); + + /**************************************************************************** + * + * Store the maximum size of the intelligent buffer. Used to convert VME to HEX. + * + *****************************************************************************/ + + if ( g_usIntelBufferSize > g_usLCOUNTSize ) { + g_usLCOUNTSize = g_usIntelBufferSize; + } + + /**************************************************************************** + * + * Copy intel data to the buffer. + * + *****************************************************************************/ + + for ( usIntelBufferIndex = 0; usIntelBufferIndex < g_usIntelBufferSize; usIntelBufferIndex++ ) { + g_pucIntelBuffer[ usIntelBufferIndex ] = GetByte(); + } + + /**************************************************************************** + * + * Set the data type register to get data from the intelligent data buffer. + * + *****************************************************************************/ + + g_usDataType |= LHEAP_IN; + + /**************************************************************************** + * + * If the HEAP_IN flag is set, temporarily unset the flag so data will be + * retrieved from the status buffer. + * + *****************************************************************************/ + + if ( g_usDataType & HEAP_IN ) { + g_usDataType &= ~HEAP_IN; + cRepeatHeap = 1; + } + +#ifdef VME_DEBUG + printf( "LCOUNT %d;\n", a_usCountSize ); +#endif //VME_DEBUG + + /**************************************************************************** + * + * Iterate through the intelligent programming command. + * + *****************************************************************************/ + + for ( usCountIndex = 0; usCountIndex < a_usCountSize; usCountIndex++ ) { + + /**************************************************************************** + * + * Initialize the intel data index to 0 before each iteration. + * + *****************************************************************************/ + + g_usIntelDataIndex = 0; + cOpcode = 0; + ucState = 0; + usDelay = 0; + usToggle = 0; + // usByte = 0; + usContinue = 1; + + /*************************************************************** + * + * Begin looping through all the VME opcodes. + * + ***************************************************************/ + /*************************************************************** + * 4/1/09 Nguyen replaced the recursive function call codes on + * the ispVMLCOUNT function + * + ***************************************************************/ + while ( usContinue ) + { + cOpcode = GetByte(); + switch ( cOpcode ) { + case HIR: + case TIR: + case HDR: + case TDR: + /*************************************************************** + * + * Set the header/trailer of the device in order to bypass + * successfully. + * + ***************************************************************/ + + ispVMAmble( cOpcode ); + break; + case STATE: + + /*************************************************************** + * + * Step the JTAG state machine. + * + ***************************************************************/ + + ucState = GetByte(); + /*************************************************************** + * + * Step the JTAG state machine to DRCAPTURE to support Looping. + * + ***************************************************************/ + + if ( (g_usDataType & LHEAP_IN) && + (ucState == DRPAUSE ) && + ( g_cCurrentJTAGState == ucState )) + { + ispVMStateMachine( DRCAPTURE ); + } + ispVMStateMachine( ucState ); +#ifdef VME_DEBUG + printf( "LDELAY %s ", GetState( ucState ) ); +#endif //VME_DEBUG + break; + case SIR: +#ifdef VME_DEBUG + printf( "SIR " ); +#endif //VME_DEBUG + /*************************************************************** + * + * Shift in data into the device. + * + ***************************************************************/ + + cRetCode = ispVMShift( cOpcode ); + break; + case SDR: + +#ifdef VME_DEBUG + printf( "LSDR " ); +#endif //VME_DEBUG + /*************************************************************** + * + * Shift in data into the device. + * + ***************************************************************/ + + cRetCode = ispVMShift( cOpcode ); + break; + case WAIT: + + /*************************************************************** + * + * Observe delay. + * + ***************************************************************/ + + usDelay = (unsigned short)ispVMDataSize(); + ispVMDelay( usDelay ); + +#ifdef VME_DEBUG + if ( usDelay & 0x8000 ) { + + /*************************************************************** + * + * Since MSB is set, the delay time must be decoded to + * millisecond. The SVF2VME encodes the MSB to represent + * millisecond. + * + ***************************************************************/ + + usDelay &= ~0x8000; + printf( "%.2E SEC;\n", ( float ) usDelay / 1000 ); + } + else { + + /*************************************************************** + * + * Since MSB is not set, the delay time is given as microseconds. + * + ***************************************************************/ + + printf( "%.2E SEC;\n", ( float ) usDelay / 1000000 ); + } +#endif //VME_DEBUG + break; + case TCK: + + /*************************************************************** + * + * Issue clock toggles. + * + ***************************************************************/ + + usToggle = (unsigned short)ispVMDataSize(); + ispVMClocks( usToggle ); + +#ifdef VME_DEBUG + printf( "RUNTEST %d TCK;\n", usToggle ); +#endif //VME_DEBUG + break; + case ENDLOOP: + + /*************************************************************** + * + * Exit point from processing loops. + * + ***************************************************************/ + usContinue = 0; + break; + + case COMMENT: + + /*************************************************************** + * + * Display comment. + * + ***************************************************************/ + + ispVMComment( ( unsigned short ) ispVMDataSize() ); + break; + case ispEN: + ucState = GetByte(); + if((ucState == ON)||(ucState == 0x01)) + writePort( g_ucPinENABLE, 0x01 ); + else + writePort( g_ucPinENABLE, 0x00 ); + ispVMDelay( 1 ); + break; + case TRST: + if(GetByte() == 0x01) + writePort( g_ucPinTRST, 0x01 ); + else + writePort( g_ucPinTRST, 0x00 ); + ispVMDelay( 1 ); + break; + default: + + /*************************************************************** + * + * Invalid opcode encountered. + * + ***************************************************************/ + +#ifdef VME_DEBUG + printf( "\nINVALID OPCODE: 0x%.2X\n", cOpcode ); +#endif //VME_DEBUG + + return VME_INVALID_FILE; + } + } + if ( cRetCode >= 0 ) { + /**************************************************************************** + * + * Break if intelligent programming is successful. + * + *****************************************************************************/ + + break; + } + + } + /**************************************************************************** + * + * If HEAP_IN flag was temporarily disabled, re-enable it before exiting. + * + *****************************************************************************/ + + if ( cRepeatHeap ) { + g_usDataType |= HEAP_IN; + } + + /**************************************************************************** + * + * Set the data type register to not get data from the intelligent data buffer. + * + *****************************************************************************/ + + g_usDataType &= ~LHEAP_IN; + return cRetCode; +} + +/*************************************************************** + * + * ispVMClocks + * + * Applies the specified number of pulses to TCK. + * + ***************************************************************/ + +void ispVMClocks( unsigned short Clocks ) +{ + unsigned short iClockIndex = 0; + for ( iClockIndex = 0; iClockIndex < Clocks; iClockIndex++ ) { + sclock(); + } +} + +/*************************************************************** + * + * ispVMBypass + * + * This procedure takes care of the HIR, HDR, TIR, TDR for the + * purpose of putting the other devices into Bypass mode. The + * current state is checked to find out if it is at DRPAUSE or + * IRPAUSE. If it is at DRPAUSE, perform bypass register scan. + * If it is at IRPAUSE, scan into instruction registers the bypass + * instruction. + * + ***************************************************************/ + +void ispVMBypass( signed char ScanType, unsigned short Bits ) +{ + //09/11/07 NN added local variables initialization + unsigned short iIndex = 0; + unsigned short iSourceIndex = 0; + unsigned char cBitState = 0; + unsigned char cCurByte = 0; + unsigned char * pcSource = NULL; + + if ( Bits <= 0 ) { + return; + } + + switch ( ScanType ) { + case HIR: + pcSource = g_pucHIRData; + break; + case TIR: + pcSource = g_pucTIRData; + break; + case HDR: + pcSource = g_pucHDRData; + break; + case TDR: + pcSource = g_pucTDRData; + break; + default: + break; + } + if(pcSource) + { + iSourceIndex = 0; + cBitState = 0; + for ( iIndex = 0; iIndex < Bits - 1; iIndex++ ) { + /* Scan instruction or bypass register */ + if ( iIndex % 8 == 0 ) { + cCurByte = pcSource[ iSourceIndex++ ]; + } + cBitState = ( unsigned char ) ( ( ( cCurByte << iIndex % 8 ) & 0x80 ) ? 0x01 : 0x00 ); + writePort( g_ucPinTDI, cBitState ); + sclock(); + } + + if ( iIndex % 8 == 0 ) { + cCurByte = pcSource[ iSourceIndex++ ]; + } + + cBitState = ( unsigned char ) ( ( ( cCurByte << iIndex % 8 ) & 0x80 ) ? 0x01 : 0x00 ); + writePort( g_ucPinTDI, cBitState ); + } +} + +/*************************************************************** + * + * ispVMStateMachine + * + * This procedure steps all devices in the daisy chain from a given + * JTAG state to the next desirable state. If the next state is TLR, + * the JTAG state machine is brute forced into TLR by driving TMS + * high and pulse TCK 6 times. + * + ***************************************************************/ + +void ispVMStateMachine( signed char cNextJTAGState ) +{ + //09/11/07 NN added local variables initialization + signed char cPathIndex = 0; + signed char cStateIndex = 0; + short int found = 0; + + if ( ( g_cCurrentJTAGState == cNextJTAGState ) && ( cNextJTAGState != RESET ) ) { + return; + } + + for ( cStateIndex = 0; cStateIndex < 25; cStateIndex++ ) { + if ( ( g_cCurrentJTAGState == g_JTAGTransistions[ cStateIndex ].CurState ) && ( cNextJTAGState == g_JTAGTransistions[cStateIndex].NextState ) ) { + found = 1; + break; + } + } + if(found) + { + g_cCurrentJTAGState = cNextJTAGState; + for ( cPathIndex = 0; cPathIndex < g_JTAGTransistions[ cStateIndex ].Pulses; cPathIndex++ ) { + if ( ( g_JTAGTransistions[ cStateIndex ].Pattern << cPathIndex ) & 0x80 ) { + writePort( g_ucPinTMS, ( unsigned char ) 0x01 ); + } + else { + writePort( g_ucPinTMS, ( unsigned char ) 0x00 ); + } + sclock(); + } + + writePort( g_ucPinTDI, 0x00 ); + writePort( g_ucPinTMS, 0x00 ); + } +} + +/*************************************************************** + * + * ispVMStart + * + * Enable the port to the device and set the state to RESET (TLR). + * + ***************************************************************/ + +void ispVMStart() +{ +#ifdef VME_DEBUG + printf( "// ISPVM EMBEDDED ADDED\n" ); + printf( "STATE RESET;\n" ); +#endif + + ispVMStateMachine( RESET ); /*step devices to RESET state*/ +} + +/*************************************************************** + * + * ispVMEnd + * + * Set the state of devices to RESET to enable the devices and disable + * the port. + * + ***************************************************************/ + +void ispVMEnd() +{ +#ifdef VME_DEBUG + printf( "// ISPVM EMBEDDED ADDED\n" ); + printf( "STATE RESET;\n" ); + printf( "RUNTEST 1.00E-001 SEC;\n" ); +#endif + + ispVMStateMachine( RESET ); /*step devices to RESET state */ + ispVMDelay( 0x8001 ); /*wake up devices*/ +} + +/*************************************************************** + * + * ispVMSend + * + * Send the TDI data stream to devices. The data stream can be + * instructions or data. + * + ***************************************************************/ + +signed char ispVMSend( unsigned short a_usiDataSize ) +{ + //09/11/07 NN added local variables initialization + unsigned short iIndex = 0; + unsigned short iInDataIndex = 0; + unsigned char cCurByte = 0; + unsigned char cBitState = 0; + + for ( iIndex = 0; iIndex < a_usiDataSize - 1; iIndex++ ) { + if ( iIndex % 8 == 0 ) { + cCurByte = g_pucInData[ iInDataIndex++ ]; + } + cBitState = ( unsigned char ) ( ( ( cCurByte << iIndex % 8 ) & 0x80 ) ? 0x01 : 0x00 ); + writePort( g_ucPinTDI, cBitState ); + sclock(); + } + + if ( iIndex % 8 == 0 ) { + /* Take care of the last bit */ + cCurByte = g_pucInData[ iInDataIndex ]; + } + + cBitState = ( unsigned char ) ( ( ( cCurByte << iIndex % 8 ) & 0x80 ) ? 0x01 : 0x00 ); + + writePort( g_ucPinTDI, cBitState ); + if ( g_usFlowControl & CASCADE ) { + /* 1/15/04 Clock in last bit for the first n-1 cascaded frames */ + sclock(); + } + + return 0; +} + +/*************************************************************** + * + * ispVMRead + * + * Read the data stream from devices and verify. + * + ***************************************************************/ + +signed char ispVMRead( unsigned short a_usiDataSize ) +{ + //09/11/07 NN added local variables initialization + unsigned short usDataSizeIndex = 0; + unsigned short usErrorCount = 0; + unsigned short usLastBitIndex = 0; + unsigned char cDataByte = 0; + unsigned char cMaskByte = 0; + unsigned char cInDataByte = 0; + unsigned char cCurBit = 0; + unsigned char cByteIndex = 0; + unsigned short usBufferIndex = 0; + unsigned char ucDisplayByte = 0x00; + unsigned char ucDisplayFlag = 0x01; + char StrChecksum[256] = {0}; + unsigned char g_usCalculateChecksum = 0x00; + + //09/11/07 NN Type cast mismatch variables + usLastBitIndex = (unsigned short)(a_usiDataSize - 1); + +#ifndef VME_DEBUG + /**************************************************************************** + * + * If mask is not all zeros, then set the display flag to 0x00, otherwise + * it shall be set to 0x01 to indicate that data read from the device shall + * be displayed. If VME_DEBUG is defined, always display data. + * + *****************************************************************************/ + + for ( usDataSizeIndex = 0; usDataSizeIndex < ( a_usiDataSize + 7 ) / 8; usDataSizeIndex++ ) { + if ( g_usDataType & MASK_DATA ) { + if ( g_pucOutMaskData[ usDataSizeIndex ] != 0x00 ) { + ucDisplayFlag = 0x00; + break; + } + } + else if ( g_usDataType & CMASK_DATA ) { + g_usCalculateChecksum = 0x01; + ucDisplayFlag = 0x00; + break; + } + else { + ucDisplayFlag = 0x00; + break; + } + } +#endif //VME_DEBUG + + /**************************************************************************** + * + * Begin shifting data in and out of the device. + * + *****************************************************************************/ + + for ( usDataSizeIndex = 0; usDataSizeIndex < a_usiDataSize; usDataSizeIndex++ ) { + if ( cByteIndex == 0 ) { + + /*************************************************************** + * + * Grab byte from TDO buffer. + * + ***************************************************************/ + + if ( g_usDataType & TDO_DATA ) { + cDataByte = g_pucOutData[ usBufferIndex ]; + } + + /*************************************************************** + * + * Grab byte from MASK buffer. + * + ***************************************************************/ + + if ( g_usDataType & MASK_DATA ) { + cMaskByte = g_pucOutMaskData[ usBufferIndex ]; + } + else { + cMaskByte = 0xFF; + } + + /*************************************************************** + * + * Grab byte from CMASK buffer. + * + ***************************************************************/ + + if ( g_usDataType & CMASK_DATA ) { + cMaskByte = 0x00; + g_usCalculateChecksum = 0x01; + } + + /*************************************************************** + * + * Grab byte from TDI buffer. + * + ***************************************************************/ + + if ( g_usDataType & TDI_DATA ) { + cInDataByte = g_pucInData[ usBufferIndex ]; + } + + usBufferIndex++; + } + + cCurBit = readPort(); + + if ( ucDisplayFlag ) { + ucDisplayByte <<= 1; + ucDisplayByte |= cCurBit; + } + + /**************************************************************************** + * + * Check if data read from port matches with expected TDO. + * + *****************************************************************************/ + + if ( g_usDataType & TDO_DATA ) { + //08/28/08 NN Added Calculate checksum support. + if( g_usCalculateChecksum ) + { + if(cCurBit == 0x01) + g_usChecksum += (1 << (g_uiChecksumIndex % 8)); + g_uiChecksumIndex++; + } + else + { + if ( ( ( ( cMaskByte << cByteIndex ) & 0x80 ) ? 0x01 : 0x00 ) ) { + if ( cCurBit != ( unsigned char ) ( ( ( cDataByte << cByteIndex ) & 0x80 ) ? 0x01 : 0x00 ) ) { + usErrorCount++; + } + } + } + } + + /**************************************************************************** + * + * Write TDI data to the port. + * + *****************************************************************************/ + + writePort( g_ucPinTDI, ( unsigned char ) ( ( ( cInDataByte << cByteIndex ) & 0x80 ) ? 0x01 : 0x00 ) ); + + if ( usDataSizeIndex < usLastBitIndex ) { + + /**************************************************************************** + * + * Clock data out from the data shift register. + * + *****************************************************************************/ + + sclock(); + } + else if ( g_usFlowControl & CASCADE ) { + + /**************************************************************************** + * + * Clock in last bit for the first N - 1 cascaded frames. + * + *****************************************************************************/ + + sclock(); + } + + /*************************************************************** + * + * Increment the byte index. If it exceeds 7, then reset it back + * to zero. + * + ***************************************************************/ + + cByteIndex++; + if ( cByteIndex >= 8 ) { + if ( ucDisplayFlag ) { + + /*************************************************************** + * + * Store displayed data in the TDO buffer. By reusing the TDO + * buffer to store displayed data, there is no need to allocate + * a buffer simply to hold display data. This will not cause any + * false verification errors because the true TDO byte has already + * been consumed. + * + ***************************************************************/ + + g_pucOutData[ usBufferIndex - 1 ] = ucDisplayByte; + ucDisplayByte = 0; + } + + cByteIndex = 0; + } + //09/12/07 Nguyen changed to display the 1 bit expected data + else if(a_usiDataSize == 1) + { + if ( ucDisplayFlag ) { + + /*************************************************************** + * + * Store displayed data in the TDO buffer. By reusing the TDO + * buffer to store displayed data, there is no need to allocate + * a buffer simply to hold display data. This will not cause any + * false verification errors because the true TDO byte has already + * been consumed. + * + ***************************************************************/ + + /**************************************************************************** + * + * Flip ucDisplayByte and store it in cDataByte. + * + *****************************************************************************/ + cDataByte = 0x00; + for ( usBufferIndex = 0; usBufferIndex < 8; usBufferIndex++ ) { + cDataByte <<= 1; + if ( ucDisplayByte & 0x01 ) { + cDataByte |= 0x01; + } + ucDisplayByte >>= 1; + } + g_pucOutData[ 0 ] = cDataByte; + ucDisplayByte = 0; + } + + cByteIndex = 0; + } + } + if ( ucDisplayFlag ) { + + /**************************************************************************** + * + * Display data read from the device. + * + *****************************************************************************/ + +#ifdef VME_DEBUG + printf( "RECIEVED TDO (" ); +#else + vme_out_string( "Display Data: 0x" ); +#endif //VME_DEBUG + + //09/11/07 NN Type cast mismatch variables + for ( usDataSizeIndex = (unsigned short)( ( a_usiDataSize + 7 ) / 8 ); usDataSizeIndex > 0 ; usDataSizeIndex-- ) { + cMaskByte = g_pucOutData[ usDataSizeIndex - 1 ]; + cDataByte = 0x00; + + /**************************************************************************** + * + * Flip cMaskByte and store it in cDataByte. + * + *****************************************************************************/ + + for ( usBufferIndex = 0; usBufferIndex < 8; usBufferIndex++ ) { + cDataByte <<= 1; + if ( cMaskByte & 0x01 ) { + cDataByte |= 0x01; + } + cMaskByte >>= 1; + } +#ifdef VME_DEBUG + printf( "%.2X", cDataByte ); + if ( ( ( ( a_usiDataSize + 7 ) / 8 ) - usDataSizeIndex ) % 40 == 39 ) { + printf( "\n\t\t" ); + } +#else + vme_out_hex( cDataByte ); +#endif //VME_DEBUG + } + +#ifdef VME_DEBUG + printf( ")\n\n" ); +#else + vme_out_string( "\n\n" ); +#endif //VME_DEBUG + //09/02/08 Nguyen changed to display the data Checksum + if(g_usChecksum != 0) + { + g_usChecksum &= 0xFFFF; + sprintf(StrChecksum,"Data Checksum: %04lx\n\n",g_usChecksum); + vme_out_string(StrChecksum); + g_usChecksum = 0; + } + } + + if ( usErrorCount > 0 ) { + if ( g_usFlowControl & VERIFYUES ) { + vme_out_string( "USERCODE verification failed. Continue programming......\n\n" ); + g_usFlowControl &= ~( VERIFYUES ); + return 0; + } + else { + +#ifdef VME_DEBUG + printf( "TOTAL ERRORS: %d\n", usErrorCount ); +#endif //VME_DEBUG + + return VME_VERIFICATION_FAILURE; + } + } + else { + if ( g_usFlowControl & VERIFYUES ) { + vme_out_string( "USERCODE verification passed. Programming aborted. \n\n" ); + g_usFlowControl &= ~( VERIFYUES ); + return 1; + } + else { + return 0; + } + } +} + +/*************************************************************** + * + * ispVMReadandSave + * + * Support dynamic I/O. + * + ***************************************************************/ + +signed char ispVMReadandSave( unsigned short int a_usiDataSize ) +{ + //09/11/07 NN added local variables initialization + unsigned short int usDataSizeIndex = 0; + unsigned short int usLastBitIndex = 0; + unsigned short int usBufferIndex = 0; + unsigned short int usOutBitIndex = 0; + unsigned short int usLVDSIndex = 0; + unsigned char cDataByte = 0; + unsigned char cDMASKByte = 0; + unsigned char cInDataByte = 0; + unsigned char cCurBit = 0; + unsigned char cByteIndex = 0; + signed char cLVDSByteIndex = 0; + + //09/11/07 NN Type cast mismatch variables + usLastBitIndex = (unsigned short) (a_usiDataSize - 1); + + /*************************************************************** + * + * Iterate through the data bits. + * + ***************************************************************/ + + for ( usDataSizeIndex = 0; usDataSizeIndex < a_usiDataSize; usDataSizeIndex++ ) { + if ( cByteIndex == 0 ) { + + /*************************************************************** + * + * Grab byte from DMASK buffer. + * + ***************************************************************/ + + if ( g_usDataType & DMASK_DATA ) { + cDMASKByte = g_pucOutDMaskData[ usBufferIndex ]; + } + else { + cDMASKByte = 0x00; + } + + /*************************************************************** + * + * Grab byte from TDI buffer. + * + ***************************************************************/ + + if ( g_usDataType & TDI_DATA ) { + cInDataByte = g_pucInData[ usBufferIndex ]; + } + + usBufferIndex++; + } + + cCurBit = readPort(); + cDataByte = ( unsigned char ) ( ( ( cInDataByte << cByteIndex ) & 0x80 ) ? 0x01 : 0x00 ); + + /*************************************************************** + * + * Initialize the byte to be zero. + * + ***************************************************************/ + + if ( usOutBitIndex % 8 == 0 ) { + g_pucOutData[ usOutBitIndex / 8 ] = 0x00; + } + + /*************************************************************** + * + * Use TDI, DMASK, and device TDO to create new TDI (actually + * stored in g_pucOutData). + * + ***************************************************************/ + + if ( ( ( ( cDMASKByte << cByteIndex ) & 0x80 ) ? 0x01 : 0x00 ) ) { + + if ( g_pLVDSList ) { + for ( usLVDSIndex = 0; usLVDSIndex < g_usLVDSPairCount; usLVDSIndex++ ) { + if ( g_pLVDSList[ usLVDSIndex ].usNegativeIndex == usDataSizeIndex ) { + g_pLVDSList[ usLVDSIndex ].ucUpdate = 0x01; + break; + } + } + } + + /*************************************************************** + * + * DMASK bit is 1, use TDI. + * + ***************************************************************/ + + g_pucOutData[ usOutBitIndex / 8 ] |= ( unsigned char ) ( ( ( cDataByte & 0x1 ) ? 0x01 : 0x00 ) << ( 7 - usOutBitIndex % 8 ) ); + } + else { + + /*************************************************************** + * + * DMASK bit is 0, use device TDO. + * + ***************************************************************/ + + g_pucOutData[ usOutBitIndex / 8 ] |= ( unsigned char ) ( ( ( cCurBit & 0x1 ) ? 0x01 : 0x00 ) << ( 7 - usOutBitIndex % 8 ) ); + } + + /*************************************************************** + * + * Shift in TDI in order to get TDO out. + * + ***************************************************************/ + + usOutBitIndex++; + writePort( g_ucPinTDI, cDataByte ); + if ( usDataSizeIndex < usLastBitIndex ) { + sclock(); + } + + /*************************************************************** + * + * Increment the byte index. If it exceeds 7, then reset it back + * to zero. + * + ***************************************************************/ + + cByteIndex++; + if ( cByteIndex >= 8 ) { + cByteIndex = 0; + } + } + + /*************************************************************** + * + * If g_pLVDSList exists and pairs need updating, then update + * the negative-pair to receive the flipped positive-pair value. + * + ***************************************************************/ + + if ( g_pLVDSList ) { + for ( usLVDSIndex = 0; usLVDSIndex < g_usLVDSPairCount; usLVDSIndex++ ) { + if ( g_pLVDSList[ usLVDSIndex ].ucUpdate ) { + + /*************************************************************** + * + * Read the positive value and flip it. + * + ***************************************************************/ + + cDataByte = ( unsigned char ) ( ( ( g_pucOutData[ g_pLVDSList[ usLVDSIndex ].usPositiveIndex / 8 ] << ( g_pLVDSList[ usLVDSIndex ].usPositiveIndex % 8 ) ) & 0x80 ) ? 0x01 : 0x00 ); + //09/11/07 NN Type cast mismatch variables + cDataByte = ( unsigned char ) (!cDataByte); + + /*************************************************************** + * + * Get the byte that needs modification. + * + ***************************************************************/ + + cInDataByte = g_pucOutData[ g_pLVDSList[ usLVDSIndex ].usNegativeIndex / 8 ]; + + if ( cDataByte ) { + + /*************************************************************** + * + * Copy over the current byte and set the negative bit to 1. + * + ***************************************************************/ + + cDataByte = 0x00; + for ( cLVDSByteIndex = 7; cLVDSByteIndex >= 0; cLVDSByteIndex-- ) { + cDataByte <<= 1; + if ( 7 - ( g_pLVDSList[ usLVDSIndex ].usNegativeIndex % 8 ) == cLVDSByteIndex ) { + + /*************************************************************** + * + * Set negative bit to 1. + * + ***************************************************************/ + + cDataByte |= 0x01; + } + else if ( cInDataByte & 0x80 ) { + cDataByte |= 0x01; + } + + cInDataByte <<= 1; + } + + /*************************************************************** + * + * Store the modified byte. + * + ***************************************************************/ + + g_pucOutData[ g_pLVDSList[ usLVDSIndex ].usNegativeIndex / 8 ] = cDataByte; + } + else { + + /*************************************************************** + * + * Copy over the current byte and set the negative bit to 0. + * + ***************************************************************/ + + cDataByte = 0x00; + for ( cLVDSByteIndex = 7; cLVDSByteIndex >= 0; cLVDSByteIndex-- ) { + cDataByte <<= 1; + if ( 7 - ( g_pLVDSList[ usLVDSIndex ].usNegativeIndex % 8 ) == cLVDSByteIndex ) { + + /*************************************************************** + * + * Set negative bit to 0. + * + ***************************************************************/ + + cDataByte |= 0x00; + } + else if ( cInDataByte & 0x80 ) { + cDataByte |= 0x01; + } + + cInDataByte <<= 1; + } + + /*************************************************************** + * + * Store the modified byte. + * + ***************************************************************/ + + g_pucOutData[ g_pLVDSList[ usLVDSIndex ].usNegativeIndex / 8 ] = cDataByte; + } + + break; + } + } + } + + return( 0 ); +} + +signed char ispVMProcessLVDS( unsigned short a_usLVDSCount ) +{ + unsigned short usLVDSIndex = 0; + + /*************************************************************** + * + * Allocate memory to hold LVDS pairs. + * + ***************************************************************/ + + ispVMMemManager( LVDS, a_usLVDSCount ); + g_usLVDSPairCount = a_usLVDSCount; + +#ifdef VME_DEBUG + printf( "LVDS %d (", a_usLVDSCount ); +#endif //VME_DEBUG + + /*************************************************************** + * + * Iterate through each given LVDS pair. + * + ***************************************************************/ + + for ( usLVDSIndex = 0; usLVDSIndex < g_usLVDSPairCount; usLVDSIndex++ ) { + + /*************************************************************** + * + * Assign the positive and negative indices of the LVDS pair. + * + ***************************************************************/ + + //09/11/07 NN Type cast mismatch variables + g_pLVDSList[ usLVDSIndex ].usPositiveIndex = (unsigned short) ispVMDataSize(); + //09/11/07 NN Type cast mismatch variables + g_pLVDSList[ usLVDSIndex ].usNegativeIndex = (unsigned short)ispVMDataSize(); + +#ifdef VME_DEBUG + if ( usLVDSIndex < g_usLVDSPairCount - 1 ) { + printf( "%d:%d, ", g_pLVDSList[ usLVDSIndex ].usPositiveIndex, g_pLVDSList[ usLVDSIndex ].usNegativeIndex ); + } + else { + printf( "%d:%d", g_pLVDSList[ usLVDSIndex ].usPositiveIndex, g_pLVDSList[ usLVDSIndex ].usNegativeIndex ); + } +#endif //VME_DEBUG + + } + +#ifdef VME_DEBUG + printf( ");\n", a_usLVDSCount ); +#endif //VME_DEBUG + + return( 0 ); +} diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/vmopcode.h b/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/vmopcode.h new file mode 100644 index 00000000000..2d0febeadc2 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/ispvme_12.2/vmopcode.h @@ -0,0 +1,233 @@ +/*************************************************************** +* +* This is the include file for Lattice Semiconductor's ispVM +* Embedded software application. +* +***************************************************************/ + +/*************************************************************** +* +* VME version. +* +* History: +* +***************************************************************/ +#define VME_VERSION_NUMBER "12.2" + +/*************************************************************** +* +* Maximum declarations. +* +***************************************************************/ + +#define VMEHEXMAX 60000L /* The hex file is split 60K per file. */ +#define SCANMAX 64000L /* The maximum SDR/SIR burst. */ + +/*************************************************************** +* +* Supported JTAG state transitions. +* +***************************************************************/ + +#define RESET 0x00 +#define IDLE 0x01 +#define IRPAUSE 0x02 +#define DRPAUSE 0x03 +#define SHIFTIR 0x04 +#define SHIFTDR 0x05 +#define DRCAPTURE 0x06 + +/*************************************************************** +* +* Flow control register bit definitions. A set bit indicates +* that the register currently exhibits the corresponding mode. +* +***************************************************************/ + +#define INTEL_PRGM 0x0001 /* Intelligent programming is in effect. */ +#define CASCADE 0x0002 /* Currently splitting large SDR. */ +#define REPEATLOOP 0x0008 /* Currently executing a repeat loop. */ +#define SHIFTRIGHT 0x0080 /* The next data stream needs a right shift. */ +#define SHIFTLEFT 0x0100 /* The next data stream needs a left shift. */ +#define VERIFYUES 0x0200 /* Continue if fail is in effect. */ + +/*************************************************************** +* +* DataType register bit definitions. A set bit indicates +* that the register currently holds the corresponding type of data. +* +***************************************************************/ + +#define EXPRESS 0x0001 /* Simultaneous program and verify. */ +#define SIR_DATA 0x0002 /* SIR is the active SVF command. */ +#define SDR_DATA 0x0004 /* SDR is the active SVF command. */ +#define COMPRESS 0x0008 /* Data is compressed. */ +#define TDI_DATA 0x0010 /* TDI data is present. */ +#define TDO_DATA 0x0020 /* TDO data is present. */ +#define MASK_DATA 0x0040 /* MASK data is present. */ +#define HEAP_IN 0x0080 /* Data is from the heap. */ +#define LHEAP_IN 0x0200 /* Data is from intel data buffer. */ +#define VARIABLE 0x0400 /* Data is from a declared variable. */ +#define CRC_DATA 0x0800 /* CRC data is pressent. */ +#define CMASK_DATA 0x1000 /* CMASK data is pressent. */ +#define RMASK_DATA 0x2000 /* RMASK data is pressent. */ +#define READ_DATA 0x4000 /* READ data is pressent. */ +#define DMASK_DATA 0x8000 /* DMASK data is pressent. */ + +/*************************************************************** +* +* Pin opcodes. +* +***************************************************************/ + +#define signalENABLE 0x1C /* ispENABLE pin. */ +#define signalTMS 0x1D /* TMS pin. */ +#define signalTCK 0x1E /* TCK pin. */ +#define signalTDI 0x1F /* TDI pin. */ +#define signalTRST 0x20 /* TRST pin. */ + +/*************************************************************** +* +* Supported vendors. +* +***************************************************************/ + +#define VENDOR 0x56 +#define LATTICE 0x01 +#define ALTERA 0x02 +#define XILINX 0x03 + +/*************************************************************** +* +* Opcode definitions. +* +* Note: opcodes must be unique. +* +***************************************************************/ + +#define ENDDATA 0x00 /* The end of the current SDR data stream. */ +#define RUNTEST 0x01 /* The duration to stay at the stable state. */ +#define ENDDR 0x02 /* The stable state after SDR. */ +#define ENDIR 0x03 /* The stable state after SIR. */ +#define ENDSTATE 0x04 /* The stable state after RUNTEST. */ +#define TRST 0x05 /* Assert the TRST pin. */ +#define HIR 0x06 /* The sum of the IR bits of the leading devices. */ +#define TIR 0x07 /* The sum of the IR bits of the trailing devices. */ +#define HDR 0x08 /* The number of leading devices. */ +#define TDR 0x09 /* The number of trailing devices. */ +#define ispEN 0x0A /* Assert the ispEN pin. */ +#define FREQUENCY 0x0B /* The maximum clock rate to run the JTAG state machine. */ +#define STATE 0x10 /* Move to the next stable state. */ +#define SIR 0x11 /* The instruction stream follows. */ +#define SDR 0x12 /* The data stream follows. */ +#define TDI 0x13 /* The following data stream feeds into the device. */ +#define TDO 0x14 /* The following data stream is compared against the device. */ +#define MASK 0x15 /* The following data stream is used as mask. */ +#define XSDR 0x16 /* The following data stream is for simultaneous program and verify. */ +#define XTDI 0x17 /* The following data stream is for shift in only. It must be stored for the next XSDR. */ +#define XTDO 0x18 /* There is not data stream. The data stream was stored from the previous XTDI. */ +#define MEM 0x19 /* The maximum memory needed to allocate in order hold one row of data. */ +#define WAIT 0x1A /* The duration of delay to observe. */ +#define TCK 0x1B /* The number of TCK pulses. */ +#define SHR 0x23 /* Set the flow control register for right shift. */ +#define SHL 0x24 /* Set the flow control register for left shift. */ +#define HEAP 0x32 /* The memory size needed to hold one loop. */ +#define REPEAT 0x33 /* The beginning of the loop. */ +#define LEFTPAREN 0x35 /* The beginning of data following the loop. */ +#define VAR 0x55 /* Plac holder for loop data. */ +#define SEC 0x1C /* The delay time in seconds that must be observed. */ +#define SMASK 0x1D /* The mask for TDI data. */ +#define MAX 0x1E /* The absolute maximum wait time. */ +#define ON 0x1F /* Assert the targeted pin. */ +#define OFF 0x20 /* Dis-assert the targeted pin. */ +#define SETFLOW 0x30 /* Change the flow control register. */ +#define RESETFLOW 0x31 /* Clear the flow control register. */ +#define CRC 0x47 /* The following data stream is used for CRC calculation. */ +#define CMASK 0x48 /* The following data stream is used as mask for CRC calculation. */ +#define RMASK 0x49 /* The following data stream is used as mask for read and save. */ +#define READ 0x50 /* The following data stream is used for read and save. */ +#define ENDLOOP 0x59 /* The end of the repeat loop. */ +#define SECUREHEAP 0x60 /* Used to secure the HEAP opcode. */ +#define VUES 0x61 /* Support continue if fail. */ +#define DMASK 0x62 /* The following data stream is used for dynamic I/O. */ +#define COMMENT 0x63 /* Support SVF comments in the VME file. */ +#define HEADER 0x64 /* Support header in VME file. */ +#define FILE_CRC 0x65 /* Support crc-protected VME file. */ +#define LCOUNT 0x66 /* Support intelligent programming. */ +#define LDELAY 0x67 /* Support intelligent programming. */ +#define LSDR 0x68 /* Support intelligent programming. */ +#define LHEAP 0x69 /* Memory needed to hold intelligent data buffer */ +#define CONTINUE 0x70 /* Allow continuation. */ +#define LVDS 0x71 /* Support LVDS. */ +#define ENDVME 0x7F /* End of the VME file. */ +#define HIGH 0x80 /* Assert the targeted pin. */ +#define LOW 0x81 /* Dis-assert the targeted pin. */ +#define ENDFILE 0xFF /* End of file. */ + +/* Denverton GPIO MAPPING */ +#define MAP_MASK (sysconf(_SC_PAGE_SIZE)-1) +#define MAP_SIZE(addr) ( (addr & MAP_MASK) + 8 ) +#define OFFSET_ADDR(addr) (addr & MAP_MASK) +#define GET_PORT(addr) ( (addr >> 16) & 0xFF ) +#define DNV_BAR 0xFD000000 +#define DNV_GPIO_TCK_CONFIG 0xFDC20510 +#define DNV_GPIO_TMS_CONFIG 0xFDC20508 +#define DNV_GPIO_TDI_CONFIG 0xFDC204D8 +#define DNV_GPIO_TDO_CONFIG 0xFDC50570 +#define DNV_GPIO_DIR_INPUT 1 +#define DNV_GPIO_DIR_OUTPUT 0 +#define DNV_GPIO_LVL_HIGH 1 +#define DNV_GPIO_LVL_LOW 0 + +// FIXME: This only works on questone2bd +#define CPU_FREQ_MH_CONFIG 2200 /* in MHz */ +#define GPIO_USE_SEL 0x500 +#define GP_IO_SEL 0x504 +#define GP_LVL 0x50C +#define GPIO_USE_SEL2 0x530 +#define GP_IO_SEL2 0x534 +#define GP_LVL2 0x538 +#define GPIO_USE_SEL3 0x540 +#define GP_IO_SEL3 0x544 +#define GP_LVL3 0x548 + +#define GPIO_TCK_CONFIG 18 +#define GPIO_TMS_CONFIG 9 +#define GPIO_TDI_CONFIG 8 +#define GPIO_TDO_CONFIG 10 +#define GPIO_ENABLE_CONFIG 13 /* GPIO 13 is unused */ +#define GPIO_TRST_CONFIG 13 /* GPIO 13 is unused */ + +/*************************************************************** +* +* ispVM Embedded Return Codes. +* +***************************************************************/ + +#define VME_VERIFICATION_FAILURE -1 +#define VME_FILE_READ_FAILURE -2 +#define VME_VERSION_FAILURE -3 +#define VME_INVALID_FILE -4 +#define VME_ARGUMENT_FAILURE -5 +#define VME_CRC_FAILURE -6 + + +/*************************************************************** +* +* Type definitions. +* +***************************************************************/ + +/* Support LVDS */ +typedef struct { + unsigned short usPositiveIndex; + unsigned short usNegativeIndex; + unsigned char ucUpdate; +} LVDSPair; + +void isp_dnv_gpio_config( unsigned int gpio, unsigned int dir ); +void isp_dnv_gpio_write( unsigned int gpio, unsigned int value); +int isp_dnv_gpio_read( unsigned int gpio); + +void isp_dnv_gpio_init(void); +void isp_dnv_gpio_deinit(void); diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/platformutil.py b/platform/broadcom/sonic-platform-modules-cel/tools/platformutil.py new file mode 100644 index 00000000000..5c73dd9870f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/platformutil.py @@ -0,0 +1,642 @@ +#!/usr/bin/env python +# +# platformutil/main.py +# +# Command-line utility for interacting with PSU in SONiC +# +# example output +# platformutil psu status +# PSU Presence Status PN SN +# PSU 1 PRESENT OK CSU550AP-3-300 M623TW004ZAAL +# PSU 2 NOT_PRESENT N/A N/A N/A +# +# platformutil fan status +# FAN Status Speed Low_thd High_thd PN SN +# FAN 1 OK 10169 RPM 300 RPM 16000 RPM M6510-FAN-F 1000000000014 +# FAN 2 NOT_OK 20000 RPM 300 RPM 16000 RPM M6510-FAN-F 1000000000014 +# +# platformutil sensor status +#Sensor InputName State Value Low_thd High_thd +#----------------- ------------------- ------- -------- --------- ---------- +#syscpld-i2c-0-0d CPU temp NOT_OK 41.0 C 0 C 0.0 C +#syscpld-i2c-0-0d Optical temp NOT_OK 26.0 C 0 C 0.0 C +#syscpld-i2c-0-0d Switch temp NOT_OK 35.0 C 0 C 0.0 C +# +# should implenmet the below classes in the specified plugin +# +# class PsuUtil: +# int get_num_psus(); //get the number of power supply units +# bool get_psu_presence(int index) //get the power status of the psu, index:1,2 +# bool get_psu_status(int index) //get the running status of the psu,index:1,2 +# str get_psu_sn(int index) //get the serial number of the psu, return value example: "M623TW004ZAAL" +# str get_psu_pn(int index) //get the product name of the psu, return value example: "CSU550AP-3-300" +# +# // Get all information of PSUs, returns JSON objects in python 'DICT'. +# // return value of get_all(): +# // Number: mandatory, max number of PSU, integer +# // PSU1, PSU2, ...: mandatory, PSU name, string +# // Present: mandatory for each PSU, present status, boolean, True for present, False for NOT present +# // PowerStatus: conditional, if PRESENT is True, power status of PSU, +# // boolean, True for powered, False for NOT powered +# // PN, conditional, if PRESENT is True, PN of the PSU, string +# // SN, conditional, if PRESENT is True, SN of the PSU, string +# // example: +# // { +# // "Number": 2, +# // "PSU1": { +# // "Present": True, +# // "PowerStatus": True, +# // "PN": "PN-EXAMPLE-123", +# // "SN": "SN-EXAMPLE-123", +# // "InputStatus": True, +# // "OutputStatus": True, +# // "InputType": "DC" +# // "AirFlow": "BTOF" +# // }, +# // "PSU2": { +# // "Present": False +# // } +# // } +# dict get_all() + +# class FanUtil: +# int get_fans_name_list(); //get the names of all the fans(FAN1-1,FAN1-2,FAN2-1,FAN2-2...) +# int get_fan_speed(int index); //get the current speed of the fan, the unit is "RPM" +# int get_fan_low_threshold(int index); //get the low speed threshold of the fan, if the current speed < low speed threshold, the status of the fan is ok. +# int get_fan_high_threshold(int index); //get the hight speed threshold of the fan, if the current speed > high speed threshold, the status of the fan is not ok +# str get_fan_pn(int index);//get the product name of the fan +# str get_fan_sn(int index);//get the serial number of the fan +# // Get all information of system FANs, returns JSON objects in python 'DICT'. +# // Number, mandatory, max number of FAN, integer +# // FAN1_1, FAN1_2, ... mandatory, FAN name, string +# // Present, mandatory for each FAN, present status, boolean, True for present, False for NOT present, read directly from h/w +# // Running, conditional, if PRESENT is True, running status of the FAN, True for running, False for stopped, read directly from h/w +# // Speed, conditional, if PRESENT is True, real FAN speed, float, read directly from h/w +# // LowThd, conditional, if PRESENT is True, lower bound of FAN speed, float, read from h/w +# // HighThd, conditional, if PRESENT is True, upper bound of FAN speed, float, read from h/w +# // PN, conditional, if PRESENT is True, PN of the FAN, string +# // SN, conditional, if PRESENT is True, SN of the FAN, string +# // Return value python 'dict' object example: +# // { +# // "Number": 3, +# // "FAN1_1": { +# // "Present": True, +# // "Running": True, +# // "Speed": 2000.0, +# // "LowThd": 1000.0, +# // "HighThd": 15000.0, +# // "PN": "PN-EXAMPLE-123", +# // "SN": "SN-EXAMPLE-123" +# // "Status": True, +# // "AirFlow": "FTOB" +# // }, +# // "FAN1_2": { +# // "Present": True, +# // "Running": True, +# // "Speed": 2500.0, +# // "LowThd": 1000.0, +# // "HighThd": 15000.0, +# // "PN": "PN-EXAMPLE-456", +# // "SN": "SN-EXAMPLE-456" +# // "Status": True, +# // "AirFlow": "BTOF" +# // }, +# // "FAN2_1": { +# // "Present": True, +# // "Running": False +# // }, +# // "FAN2_2": { +# // "Present": True, +# // "Running": False +# // }, +# // "FAN3_1": { +# // "Present": False +# // }, +# // "FAN3_2": { +# // "Present": False +# // } +# // } +# dict get_all() +# +# class SensorUtil: +# int get_num_sensors(); //get the number of sensors +# int get_sensor_input_num(int index); //get the number of the input items of the specified sensor +# str get_sensor_name(int index);// get the device name of the specified sensor.for example "coretemp-isa-0000" +# str get_sensor_input_name(int sensor_index, int input_index); //get the input item name of the specified input item of the specified sensor index, for example "Physical id 0" +# str get_sensor_input_type(int sensor_index, int input_index); //get the item type of the specified input item of the specified sensor index, the return value should +# //among "voltage","temperature"... +# float get_sensor_input_value(int sensor_index, int input_index);//get the current value of the input item, the unit is "V" or "C"... +# float get_sensor_input_low_threshold(int sensor_index, int input_index); //get the low threshold of the value, the status of this item is not ok if the current +# //value high_threshold +# // Get all information of system sensors, returns JSON objects in python 'DICT'. +# // SensorName1, SensorName2, ... optional, string +# // SensorInput1, SensorInput2, ... optional, string +# // Type, mandatory in SensorInput$INDEX, should be on of { "temperature", "voltage", "power", "amp", "RPM" } +# // Value, mandatory in SensorInput$INDEX, float , real value +# // LowThd, mandatory in SensorInput$INDEX, float , lower bound of value +# // HighThd, mandatory in SensorInput$INDEX, float , upper bound of value +# // Return python 'dict' objects, example: +# // { +# // "SensorName1": { +# // "CPU_TEMP": +# // "Type": "temperature", +# // "Value": 37.3, +# // "LowThd": 0.0, +# // "HighThd": 110.0 +# // }, +# // "SWITCH_TEMP": { +# // "Type": "temperature", +# // "Value": 45.2, +# // "LowThd": 0.0, +# // "HighThd": 108.0 +# // }, +# // "INLET_TEMP": { +# // "Type": "temperature", +# // "Value": 22.0, +# // "LowThd": 0.0, +# // "HighThd": 70.0 +# // }, +# // "Sys_AirFlow": "BTOF", +# // "Switch_VDDCore_0.8v": { +# // "Type": "voltage", +# // "Value": 0.75, +# // "LowThd": 0.7, +# // "HighThd": 0.85 +# // }, +# // "Cpu_VDDCore_0.8v": { +# // "Type": "voltage", +# // "Value": 0.75, +# // "LowThd": 0.7, +# // "HighThd": 0.85 +# // }, +# // "SensorInput1": { +# // "Type": "temperature", +# // "Value": 30.0, +# // "LowThd": 0.0, +# // "HighThd": 100.0" +# // }, +# // "SensorInput2": { +# // "Type": "voltage", +# // "Value": 0.5, +# // "LowThd": 0.0, +# // "HighThd": 1.5 +# // }, +# // "SensorInput3": { +# // "Type": "power", +# // "Value": 2.5, +# // "LowThd": 0.0, +# // "HighThd": 5.0 +# // } +# // }, +# // "SensorName2": { +# // "SensorInput1": { +# // "Type": "RPM", +# // "Value": 2000.0, +# // "LowThd": 1000.0, +# // "HighThd": 15000.0 +# // }, +# // "SensorInputName2": { +# // "Type": "amp", +# // "Value": 0.1, +# // "LowThd": 0.0, +# // "HighThd": 0.3 +# // } +# // } +# // } + +try: + import sys + import os + import subprocess + import click + import imp + import syslog + import types + import traceback + from tabulate import tabulate +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + +VERSION = '1.2' + +SYSLOG_IDENTIFIER = "platformutil" +PLATFORM_PSU_MODULE_NAME = "psuutil" +PLATFORM_PSU_CLASS_NAME = "PsuUtil" + +#gongjian add +PLATFORM_SENSOR_MODULE_NAME = "sensorutil" +PLATFORM_SENSOR_CLASS_NAME = "SensorUtil" + +PLATFORM_FAN_MODULE_NAME = "fanutil" +PLATFORM_FAN_CLASS_NAME = "FanUtil" +#end gongjian add + +PLATFORM_ROOT_PATH = '/usr/share/sonic/device' +PLATFORM_ROOT_PATH_DOCKER = '/usr/share/sonic/platform' +SONIC_CFGGEN_PATH = '/usr/local/bin/sonic-cfggen' +MINIGRAPH_PATH = '/etc/sonic/minigraph.xml' +HWSKU_KEY = "DEVICE_METADATA['localhost']['hwsku']" +PLATFORM_KEY = "DEVICE_METADATA['localhost']['platform']" + +# Global platform-specific psuutil class instance +platform_psuutil = None + +#gongjian add +platform_sensorutil = None +Platform_fanutil = None +#end gongjian add + +# ========================== Syslog wrappers ========================== + + +def log_info(msg, also_print_to_console=False): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_INFO, msg) + syslog.closelog() + + if also_print_to_console: + click.echo(msg) + + +def log_warning(msg, also_print_to_console=False): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_WARNING, msg) + syslog.closelog() + + if also_print_to_console: + click.echo(msg) + + +def log_error(msg, also_print_to_console=False): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + if also_print_to_console: + click.echo(msg) + + +# ==================== Methods for initialization ==================== + +# Returns platform and HW SKU +def get_platform_and_hwsku(): + try: + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-H', '-v', PLATFORM_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + platform = stdout.rstrip('\n') + + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-d', '-v', HWSKU_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + hwsku = stdout.rstrip('\n') + except OSError, e: + raise OSError("Cannot detect platform") + + return (platform, hwsku) + + +# Loads platform specific psuutil module from source +def load_platform_util(): + global platform_psuutil + #gongjian add + global platform_sensorutil + global platform_fanutil + + # Get platform and hwsku + (platform, hwsku) = get_platform_and_hwsku() + + # Load platform module from source + platform_path = '' + if len(platform) != 0: + platform_path = "/".join([PLATFORM_ROOT_PATH, platform]) + else: + platform_path = PLATFORM_ROOT_PATH_DOCKER + hwsku_path = "/".join([platform_path, hwsku]) + + try: + module_file_psu = "/".join([platform_path, "plugins", PLATFORM_PSU_MODULE_NAME + ".py"]) + module_psu = imp.load_source(PLATFORM_PSU_MODULE_NAME, module_file_psu) + except IOError, e: + log_error("Failed to load platform module '%s': %s" % (PLATFORM_PSU_MODULE_NAME, str(e)), True) + return -1 + + try: + platform_psuutil_class = getattr(module_psu, PLATFORM_PSU_CLASS_NAME) + platform_psuutil = platform_psuutil_class() + except AttributeError, e: + log_error("Failed to instantiate '%s' class: %s" % (PLATFORM_PSU_CLASS_NAME, str(e)), True) + return -2 + + + #gongjian add + try: + module_file_sensor = "/".join([platform_path, "plugins", PLATFORM_SENSOR_MODULE_NAME + ".py"]) + module_sensor = imp.load_source(PLATFORM_SENSOR_MODULE_NAME, module_file_sensor) + except IOError, e: + log_error("Failed to load platform module '%s': %s" % (PLATFORM_SENSOR_MODULE_NAME, str(e)), True) + return -1 + + try: + platform_sensorutil_class = getattr(module_sensor, PLATFORM_SENSOR_CLASS_NAME) + platform_sensorutil = platform_sensorutil_class() + except AttributeError, e: + log_error("Failed to instantiate '%s' class: %s" % (PLATFORM_SENSOR_CLASS_NAME, str(e)), True) + return -2 + + try: + module_file_fan = "/".join([platform_path, "plugins", PLATFORM_FAN_MODULE_NAME + ".py"]) + module_fan = imp.load_source(PLATFORM_FAN_MODULE_NAME, module_file_fan) + except IOError, e: + log_error("Failed to load platform module '%s': %s" % (PLATFORM_FAN_MODULE_NAME, str(e)), True) + return -1 + + try: + platform_fanutil_class = getattr(module_fan, PLATFORM_FAN_CLASS_NAME) + platform_fanutil = platform_fanutil_class() + except AttributeError, e: + log_error("Failed to instantiate '%s' class: %s" % (PLATFORM_FAN_CLASS_NAME, str(e)), True) + return -2 + #end gongjian add + return 0 + + +# ==================== CLI commands and groups ==================== + + +# This is our main entrypoint - the main 'psuutil' command +@click.group() +def cli(): + """platformutil - Command line utility for providing platform status""" + + if os.geteuid() != 0: + click.echo("Root privileges are required for this operation") + sys.exit(1) + + # Load platform-specific psuutil, fanutil and sensorutil class + err = load_platform_util() + if err != 0: + sys.exit(2) + +#'fan' subcommand +@cli.group() +@click.pass_context +def fan(ctx): + """fan state""" + ctx.obj = "fan" + +# 'sensor' subcommand +@cli.group() +@click.pass_context +def sensor(ctx): + """sensor state""" + ctx.obj = "sensor" + +# 'psu' subcommand +@cli.group() +@click.pass_context +def psu(ctx): + """psu state""" + ctx.obj = "psu" + +# 'version' subcommand +@cli.command() +def version(): + """Display version info""" + click.echo("platformutil version {0}".format(VERSION)) + + +# 'num' subcommand +@click.command() +@click.pass_context +def num(ctx): + """Display number of supported sensor/fan/psu device""" + if ctx.obj == "psu": + click.echo(str(platform_psuutil.get_num_psus())) + if ctx.obj == "fan": + click.echo(str(len(platform_fanutil.get_fans_name_list()))) + if ctx.obj == "sensor": + click.echo(str(platform_sensorutil.get_num_sensors())) + +psu.add_command(num) +sensor.add_command(num) +fan.add_command(num) + +# 'status' subcommand +#all API should return "N/A" or float("-intf") if not supported +@click.command() +@click.pass_context +def status(ctx): + if ctx.obj == 'psu': + psu_dict = platform_psuutil.get_all() + if psu_dict == None: + print 'Error: psuutil.get_all() failed' + return + + psu_nr = psu_dict.get('Number') + if psu_nr == None: + print 'Error: PSU get all format invalid, prop "Number" missing.' + return + + psu_names = [ k for k in psu_dict.keys() if cmp('Number', k) != 0 ] + psu_names.sort() + header = ['PSU', 'Presence', 'InputStatus', 'InputType', 'OutputStatus', 'PN', 'SN', 'AirFlow'] + status_table = [] + for psu_name in psu_names: + psu = psu_dict[psu_name] + presence = psu.get('Present') + pn = psu.get('PN') + sn = psu.get('SN') + in_status = psu.get('InputStatus') + out_status = psu.get('OutputStatus') + in_type = psu.get('InputType') + airflow = psu.get('AirFlow') + + if presence == None: + print 'Error: PSU get all format invaid, prop "Present" is missing.' + continue + elif presence == False: + presence = 'NOT_PRESENT' + in_status = 'N/A' + out_status = 'N/A' + in_type = 'N/A' + pn = 'N/A' + pn = 'N/A' + else: + presence = 'PRESENT' + if in_status == None: + in_status = 'N/A' + elif in_status == True: + in_status = 'OK' + else: + in_status = 'NOT_OK' + + if in_type == None: + in_type = 'N/A' + + if out_status == None: + out_status = 'N/A' + elif out_status == True: + out_status = 'OK' + else: + out_status = 'NOT_OK' + + if pn == None: + pn = 'N/A' + if sn == None: + sn = 'N/A' + if airflow == None: + airflow = 'N/A' + status_table.append([psu_name, presence, in_status, in_type, out_status, pn, sn, airflow]) + + if len(status_table) != psu_nr: + print 'Error: PSU get all missing some PSU information.' + + if len(status_table) > 0: + click.echo(tabulate(status_table, header, tablefmt='simple')) + + if ctx.obj == 'fan': + fan_dict = platform_fanutil.get_all() + if fan_dict == None: + print 'Error: fanutil.get_all() failed' + return + + fan_nr = fan_dict.get('Number') + if fan_nr == None: + print 'Error: FAN get all format invalid, prop "Number" missing.' + return + + header = [ 'FAN', 'Presence', 'Status', 'Speed', 'LowThd', 'HighThd', 'PN', 'SN', 'AirFlow' ] + status_table = [] + fan_names = [ k for k in fan_dict.keys() if cmp('Number', k) != 0 ] + fan_names.sort() + for fan_name in fan_names: + fan = fan_dict[fan_name] + presence = fan.get('Present') + speed = fan.get('Speed') + low = fan.get('LowThd') + high = fan.get('HighThd') + pn = fan.get('PN') + sn = fan.get('SN') + status = fan.get('Status') + airflow = fan.get('AirFlow') + + if presence == None: + print 'Error: FAN get all format invaid, prop "Present" missing.' + continue + elif presence == False: + presence = 'NOT_PRESENT' + status = 'N/A' + speed = 'N/A' + low = 'N/A' + high = 'N/A' + pn = 'N/A' + sn = 'N/A' + airflow = 'N/A' + else: + presence = 'PRESENT' + if status == None: + status = 'N/A' + elif status == True: + status = 'OK' + else: + status = 'NOT_OK' + if airflow == None: + airflow = 'N/A' + if speed == None: + speed = 'N/A' + if low == None: + low = 'N/A' + if high == None: + high = 'N/A' + if pn == None: + pn = 'N/A' + if sn == None: + sn = 'N/A' + + status_table.append([fan_name, presence, status, speed, low, high, pn, sn, airflow]) + + if len(status_table) != fan_nr: + print 'Error: FAN get all missing some FAN information.' + + if len(status_table) > 0: + click.echo(tabulate(status_table, header, tablefmt='simple')) + + if ctx.obj == 'sensor': + sensor_dict = platform_sensorutil.get_all() + if sensor_dict == None: + print 'Error: sensors.get_all() failed' + return + + header = [ 'Sensor', 'InputName', 'State', 'Value', 'LowThd', 'HighThd' ] + status_table = [] + type2unit = { 'temperature' : ' C', 'voltage' : ' V', 'RPM' : ' RPM', 'amp' : ' A', 'power' : ' W'} + type_keys = type2unit.keys() + for sensor_name, sensor_obj in sensor_dict.items(): + if cmp(sensor_name, 'Number') == 0: + continue + + si_names = [ k for k in sensor_obj.keys() ] + si_names.sort() + for si_name in si_names: + si = sensor_obj[si_name] + if si_name == "Sys_AirFlow": + status = 'OK' + airflow = si + status_table.append([sensor_name, si_name, status, airflow, airflow, airflow]) + continue + + stype = si.get('Type') + sval = si.get('Value') + slow = si.get('LowThd') + shigh = si.get('HighThd') + sunit = ' ' + fault = False + if stype != None: + sunit = type2unit.get(stype) + if sunit == None: + sunit = ' ' + try: + sval = float(sval) + except: + sval = 0.0 + fault = True + + try: + slow = float(slow) + except: + slow = 0.0 + fault = True + + try: + shigh = float(shigh) + except: + shigh = 0.0 + fault = True + + status = 'NOT_OK' + if fault == False and sval > slow and sval < shigh: + status = 'OK' + + status_table.append([sensor_name, si_name, status, (str(sval)+sunit), (str(slow)+sunit), (str(shigh)+sunit)]) + + if len(status_table) > 0: + click.echo(tabulate(status_table, header, tablefmt="simple")) + + return + +psu.add_command(status) +sensor.add_command(status) +fan.add_command(status) + + +if __name__ == '__main__': + cli() diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/power_utils/power b/platform/broadcom/sonic-platform-modules-cel/tools/power_utils/power new file mode 100755 index 00000000000..e3d42d5c035 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/power_utils/power @@ -0,0 +1,86 @@ +#!/bin/bash +# +# Copyright 2019-present Celestica. All Rights Reserved. +# +# This program file 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; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin + +prog="$0" + +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root" + exit 1 +fi + +usage() { + echo "Usage: power " + echo + echo "Commands:" + echo + echo " cycle cpu: To power cycle the CPU" + echo + echo " cycle system : To reboot the whole system" + echo +} + +cpu_cycle() { + echo "Power cycling CPU..." + curl -m 5 -d '{"data":"/usr/local/bin/wedge_power.sh off;/usr/local/bin/wedge_power.sh on"}' http://240.1.1.1:8080/api/sys/raw + ret=$? + if [ $ret -ne 0 ]; then + echo "Failed to power cycle the CPU" + fi + return 0 +} + +system_cycle() { + echo "Power cycling system..." + curl -m 5 -d '{"data":"/usr/local/bin/wedge_power.sh off;/usr/local/bin/wedge_power.sh on;reboot"}' http://240.1.1.1:8080/api/sys/raw + ret=$? + if [ $ret -ne 0 ]; then + echo "Failed to power cycle the system" + fi + return 0 +} + +if [ $# -lt 1 ]; then + usage + exit -1 +fi + +command="$1" +component="$2" +shift + +case "$command" in +cycle) + case "$component" in + cpu) + cpu_cycle + ;; + system) + system_cycle + ;; + *) + usage + exit -1 + ;; + esac + ;; +*) + usage + exit -1 + ;; +esac + +exit $? diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/read_optic_temp.py b/platform/broadcom/sonic-platform-modules-cel/tools/read_optic_temp.py new file mode 100644 index 00000000000..97e4641066b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/read_optic_temp.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +# +# read_optic_temp.py +# +# Command-line utility for read the temperature of optic modules. +# + +try: + import sys + import os + import subprocess + import click + import imp + import multiprocessing.pool + import threading +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + + +PLATFORM_ROOT_PATH = '/usr/share/sonic/device' +SONIC_CFGGEN_PATH = '/usr/local/bin/sonic-cfggen' +HWSKU_KEY = 'DEVICE_METADATA.localhost.hwsku' +PLATFORM_KEY = 'DEVICE_METADATA.localhost.platform' + +PLATFORM_SPECIFIC_SFP_MODULE_NAME = "sfputil" +PLATFORM_SPECIFIC_SFP_CLASS_NAME = "SfpUtil" + +PLATFORM_SPECIFIC_OPTICTEMP_MODULE_NAME = "optictemputil" +PLATFORM_SPECIFIC_OPTICTEMP_CLASS_NAME = "OpticTempUtil" + +# Global platform-specific psuutil class instance +platform_optictemputil = None +platform_sfputil = None + + +# ==================== Methods for initialization ==================== + +# Returns platform and HW SKU +def get_platform_and_hwsku(): + try: + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-H', '-v', PLATFORM_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + platform = stdout.rstrip('\n') + + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-d', '-v', HWSKU_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + hwsku = stdout.rstrip('\n') + except OSError, e: + raise OSError("Cannot detect platform") + + return (platform, hwsku) + + +# Returns path to port config file +def get_path_to_port_config_file(): + # Get platform and hwsku + (platform, hwsku) = get_platform_and_hwsku() + + # Load platform module from source + platform_path = "/".join([PLATFORM_ROOT_PATH, platform]) + hwsku_path = "/".join([platform_path, hwsku]) + + # First check for the presence of the new 'port_config.ini' file + port_config_file_path = "/".join([hwsku_path, "port_config.ini"]) + if not os.path.isfile(port_config_file_path): + # port_config.ini doesn't exist. Try loading the legacy 'portmap.ini' file + port_config_file_path = "/".join([hwsku_path, "portmap.ini"]) + + return port_config_file_path + + +def load_platform_util(module_name, class_name): + + # Get platform and hwsku + (platform, hwsku) = get_platform_and_hwsku() + + # Load platform module from source + platform_path = "/".join([PLATFORM_ROOT_PATH, platform]) + hwsku_path = "/".join([platform_path, hwsku]) + + try: + module_file = "/".join([platform_path, "plugins", module_name + ".py"]) + module = imp.load_source(module_name, module_file) + except IOError, e: + print("Failed to load platform module '%s': %s" % (module_name, str(e))) + sys.exit(1) + + try: + platform_util_class = getattr(module, class_name) + platform_util = platform_util_class() + except AttributeError, e: + print("Failed to instantiate '%s' class: %s" % (class_name, str(e))) + sys.exit(1) + + return platform_util + + +def get_optic_temp(port_list): + temp_list = [] + for idx, port_num in enumerate(port_list[0]): + temp = platform_optictemputil.get_optic_temp( + port_num, port_list[1][idx]) + temp_list.append(round(float(temp), 2)) + return temp_list + + +# ========================= CLI commands ========================= + +# This is our main entrypoint - the main 'opticutil' command +@click.command() +@click.option('--port_num', '-p', type=int, help='Specific port number') +def cli(port_num): + """optictemputil - Command line utility for providing platform status""" + + # Check root privileges + if os.geteuid() != 0: + click.echo("Root privileges are required for this operation") + sys.exit(1) + + global platform_optictemputil + global platform_sfputil + + # Load platform-specific class + platform_sfputil = load_platform_util( + PLATFORM_SPECIFIC_SFP_MODULE_NAME, PLATFORM_SPECIFIC_SFP_CLASS_NAME) + platform_optictemputil = load_platform_util( + PLATFORM_SPECIFIC_OPTICTEMP_MODULE_NAME, PLATFORM_SPECIFIC_OPTICTEMP_CLASS_NAME) + + # Load port config + port_config_file_path = get_path_to_port_config_file() + platform_sfputil.read_porttab_mappings(port_config_file_path) + port_list = platform_sfputil.port_to_i2cbus_mapping + port_eeprom_list = platform_sfputil.port_to_eeprom_mapping + qsfp_port_list = platform_sfputil.qsfp_ports + + port_dict = {} + temp_list = [0] + i2c_block_size = 32 + concurrent = 10 + + port_data_list = [] + port_bus_list = [] + port_type_list = [] + + # Read port temperature + if port_num: + if port_num not in port_list: + click.echo("Invalid port") + sys.exit(1) + port_list = {port_num: port_list.get(port_num)} + + for port_num, bus_num in port_list.items(): + port_type = "QSFP" if port_num in qsfp_port_list else "SFP" + port_bus_list.append(port_eeprom_list[port_num]) + port_type_list.append(port_type) + if len(port_bus_list) >= i2c_block_size: + port_tub = (port_bus_list, port_type_list) + port_data_list.append(port_tub) + port_bus_list = [] + port_type_list = [] + + if port_bus_list != []: + port_tub = (port_bus_list, port_type_list) + port_data_list.append(port_tub) + + pool = multiprocessing.pool.ThreadPool(processes=concurrent) + temp_list = pool.map(get_optic_temp, port_data_list, chunksize=1) + pool.close() + + flat_list = [item for sublist in temp_list for item in sublist] + click.echo("| PORT_NO\t| PORT_TYPE\t| TEMPERATURE\t|") + for port_num, bus_num in port_list.items(): + port_type = "QSFP" if port_num in qsfp_port_list else "SFP" + temp_idx = port_list.keys().index(port_num) + temp = flat_list[temp_idx] + click.echo('| {}\t\t| {}\t\t| {}\t\t|'.format( + port_num, port_type, temp)) + + +if __name__ == '__main__': + cli() diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/bmc_vlan.service b/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/bmc_vlan.service new file mode 100644 index 00000000000..9aeeead7239 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/bmc_vlan.service @@ -0,0 +1,12 @@ +[Unit] +Description="Service for add vlan for rsyslog" +After=network.target +Before=rsyslog-config.service +Before=ntp.service + +[Service] +Type=forking +ExecStart=/bin/sh /usr/local/etc/bmc_vlan.sh + +[Install] +WantedBy=rsyslog.service \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/bmc_vlan.sh b/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/bmc_vlan.sh new file mode 100644 index 00000000000..1d439915efb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/bmc_vlan.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Add vlan +ip link add link eth0 name eth0.4088 type vlan id 4088 +ip addr add 240.1.1.2/30 dev eth0.4088 +ip link set eth0.4088 up +exit 0 \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/sync_bmc.py b/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/sync_bmc.py new file mode 100644 index 00000000000..3065e8a21eb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/sync_bmc.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python + +############################################################################# +# # +# Platform and model specific service for send data to BMC # +# # +# # +############################################################################# + +import subprocess +import requests +import os +import imp +import multiprocessing.pool +import threading + + +PLATFORM_ROOT_PATH = '/usr/share/sonic/device' +SONIC_CFGGEN_PATH = '/usr/local/bin/sonic-cfggen' +HWSKU_KEY = 'DEVICE_METADATA.localhost.hwsku' +PLATFORM_KEY = 'DEVICE_METADATA.localhost.platform' +TEMP_URL = 'http://240.1.1.1:8080/api/sys/temp' + +PLATFORM_SPECIFIC_SFP_MODULE_NAME = "sfputil" +PLATFORM_SPECIFIC_SFP_CLASS_NAME = "SfpUtil" + +PLATFORM_SPECIFIC_OPTICTEMP_MODULE_NAME = "optictemputil" +PLATFORM_SPECIFIC_OPTICTEMP_CLASS_NAME = "OpticTempUtil" + +PLATFORM_SPECIFIC_CPUTEMP_MODULE_NAME = "cputemputil" +PLATFORM_SPECIFIC_CPUTEMP_CLASS_NAME = "CpuTempUtil" + +platform_sfputil = None +platform_optictemputil = None +platform_cputemputil = None + + +# Returns platform and HW SKU +def get_platform_and_hwsku(): + try: + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-H', '-v', PLATFORM_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + platform = stdout.rstrip('\n') + + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-d', '-v', HWSKU_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + hwsku = stdout.rstrip('\n') + except OSError, e: + raise OSError("Cannot detect platform") + + return (platform, hwsku) + + +# Returns path to port config file +def get_path_to_port_config_file(): + # Get platform and hwsku + (platform, hwsku) = get_platform_and_hwsku() + + # Load platform module from source + platform_path = "/".join([PLATFORM_ROOT_PATH, platform]) + hwsku_path = "/".join([platform_path, hwsku]) + + # First check for the presence of the new 'port_config.ini' file + port_config_file_path = "/".join([hwsku_path, "port_config.ini"]) + if not os.path.isfile(port_config_file_path): + # port_config.ini doesn't exist. Try loading the legacy 'portmap.ini' file + port_config_file_path = "/".join([hwsku_path, "portmap.ini"]) + + return port_config_file_path + + +def load_platform_util(module_name, class_name): + + # Get platform and hwsku + (platform, hwsku) = get_platform_and_hwsku() + + # Load platform module from source + platform_path = "/".join([PLATFORM_ROOT_PATH, platform]) + hwsku_path = "/".join([platform_path, hwsku]) + + try: + module_file = "/".join([platform_path, "plugins", module_name + ".py"]) + module = imp.load_source(module_name, module_file) + except IOError, e: + print("Failed to load platform module '%s': %s" % ( + module_name, str(e)), True) + return -1 + + try: + platform_util_class = getattr(module, class_name) + platform_util = platform_util_class() + except AttributeError, e: + print("Failed to instantiate '%s' class: %s" % + (class_name, str(e)), True) + return -2 + + return platform_util + + +def get_optic_temp(port_list): + temp_list = [] + for idx, port_eeprom in enumerate(port_list[0]): + temp = platform_optictemputil.get_optic_temp( + port_eeprom, port_list[1][idx]) if port_list[2][idx] else 0 + temp_list.append(round(float(temp), 2)) + return max(temp_list) + + +def get_max_optic_temp(): + port_config_file_path = get_path_to_port_config_file() + platform_sfputil.read_porttab_mappings(port_config_file_path) + port_list = platform_sfputil.port_to_i2cbus_mapping + port_eeprom_list = platform_sfputil.port_to_eeprom_mapping + qsfp_port_list = platform_sfputil.qsfp_ports + + port_data_list = [] + temp_list = [0] + i2c_block_size = 32 + concurrent = 10 + + port_bus_list = [] + port_type_list = [] + port_presence_list = [] + + for port_num, bus_num in port_list.items(): + port_type = "QSFP" if port_num in qsfp_port_list else "SFP" + port_bus_list.append(port_eeprom_list[port_num]) + port_type_list.append(port_type) + status = platform_sfputil.get_presence(port_num) + port_presence_list.append(status) + if len(port_bus_list) >= i2c_block_size: + port_tub = (port_bus_list, port_type_list, port_presence_list) + port_data_list.append(port_tub) + port_bus_list = [] + port_type_list = [] + port_presence_list = [] + + if port_bus_list != []: + port_tub = (port_bus_list, port_type_list, port_presence_list) + port_data_list.append(port_tub) + + pool = multiprocessing.pool.ThreadPool(processes=concurrent) + temp_list = pool.map(get_optic_temp, port_data_list, chunksize=1) + pool.close() + return max(temp_list) + + +# Send CPU temperature to BMC. +def send_cpu_temp(): + max_cpu_tmp = platform_cputemputil.get_max_cpu_tmp() + json_input = { + "chip": "cpu", + "option": "input", + "value": str(int(max_cpu_tmp)) + } + print "send ", json_input + requests.post(TEMP_URL, json=json_input) + + +# Send maximum optic module temperature to BMC. +def send_optic_temp(): + max_optic_temp = get_max_optic_temp() + json_input = { + "chip": "optical", + "option": "input", + "value": str(int(max_optic_temp)) + } + print "send ", json_input + requests.post(TEMP_URL, json=json_input) + + +def main(): + global platform_sfputil + global platform_cputemputil + global platform_optictemputil + + try: + platform_sfputil = load_platform_util( + PLATFORM_SPECIFIC_SFP_MODULE_NAME, PLATFORM_SPECIFIC_SFP_CLASS_NAME) + platform_cputemputil = load_platform_util( + PLATFORM_SPECIFIC_CPUTEMP_MODULE_NAME, PLATFORM_SPECIFIC_CPUTEMP_CLASS_NAME) + platform_optictemputil = load_platform_util( + PLATFORM_SPECIFIC_OPTICTEMP_MODULE_NAME, PLATFORM_SPECIFIC_OPTICTEMP_CLASS_NAME) + + t1 = threading.Thread(target=send_cpu_temp) + t2 = threading.Thread(target=send_optic_temp) + t1.start() + t2.start() + except Exception, e: + print e + pass + + +if __name__ == "__main__": + main() diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/sync_bmc.service b/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/sync_bmc.service new file mode 100644 index 00000000000..33448534251 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/sync_bmc.service @@ -0,0 +1,10 @@ +[Unit] +Description=Service for send sensor data value to BMC. +After=multi-user.target + +[Service] +Type=idle +ExecStart=/usr/bin/python /usr/local/etc/sync_bmc.py + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/sync_bmc.timer b/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/sync_bmc.timer new file mode 100644 index 00000000000..71666cc9988 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/tools/sync_bmc/sync_bmc.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Timer send sensor data value to BMC. + +[Timer] +OnUnitActiveSec=5s +OnBootSec=5s +AccuracySec=1us + +[Install] +WantedBy=timers.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 370a920d218..f0c46570a9b 100644 --- a/src/sonic-device-data/tests/permitted_list +++ b/src/sonic-device-data/tests/permitted_list @@ -209,3 +209,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