Skip to content

Commit

Permalink
DellEMC: N3248PXE Initial platform commit (#8562)
Browse files Browse the repository at this point in the history
Why I did it
Added support for the device N3248PXE

How I did it
Implemented the support for the platform N3248PXE
n3248pxe_unit_test_log.txt

Switch Vendor: DellEMC
* Switch SKU: N3248PXE
* ASIC Vendor: Broadcom
* SONiC Image: sonic-broadcom.bin

How to verify it
Verified the show platform commands
arunlk-dell authored Sep 25, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 5324ce8 commit b0b0ba8
Showing 54 changed files with 6,876 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{%- set default_topo = 't0' %}
{%- include 'buffers_config.j2' %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{%- set default_cable = '5m' %}

{%- macro generate_port_lists(PORT_ALL) %}
{# Generate list of ports #}
{% for port_idx in range(0,32) %}
{% if PORT_ALL.append("Ethernet%d" % (port_idx * 4)) %}{% endif %}
{% endfor %}
{%- endmacro %}

{%- macro generate_buffer_pool_and_profiles() %}
"BUFFER_POOL": {
"ingress_lossless_pool": {
"size": "8192000",
"type": "ingress",
"mode": "dynamic",
"xoff": "196608"
},
"egress_lossless_pool": {
"size": "8388608",
"type": "egress",
"mode": "static"
}
},
"BUFFER_PROFILE": {
"ingress_lossy_profile": {
"pool":"[BUFFER_POOL|ingress_lossless_pool]",
"size":"0",
"dynamic_th":"3"
},
"egress_lossless_profile": {
"pool":"[BUFFER_POOL|egress_lossless_pool]",
"size":"0",
"static_th":"8388608"
},
"egress_lossy_profile": {
"pool":"[BUFFER_POOL|egress_lossless_pool]",
"size":"1518",
"dynamic_th":"3"
}
},
{%- endmacro %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{%- set default_cable = '300m' %}

{%- macro generate_port_lists(PORT_ALL) %}
{# Generate list of ports #}
{% for port_idx in range(0,32) %}
{% if PORT_ALL.append("Ethernet%d" % (port_idx * 4)) %}{% endif %}
{% endfor %}
{%- endmacro %}

{%- macro generate_buffer_pool_and_profiles() %}
"BUFFER_POOL": {
"ingress_lossless_pool": {
"size": "8192000",
"type": "ingress",
"mode": "dynamic",
"xoff": "196608"
},
"egress_lossless_pool": {
"size": "8388608",
"type": "egress",
"mode": "static"
}
},
"BUFFER_PROFILE": {
"ingress_lossy_profile": {
"pool":"[BUFFER_POOL|ingress_lossless_pool]",
"size":"0",
"dynamic_th":"3"
},
"egress_lossless_profile": {
"pool":"[BUFFER_POOL|egress_lossless_pool]",
"size":"0",
"static_th":"8388608"
},
"egress_lossy_profile": {
"pool":"[BUFFER_POOL|egress_lossless_pool]",
"size":"1518",
"dynamic_th":"3"
}
},
{%- endmacro %}
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# PG lossless profiles.
# speed cable size xon xoff threshold xon_offset
10000 5m 9427 0 50176 1 3584
25000 5m 9427 0 50176 1 3584
40000 5m 9427 0 50176 1 3584
50000 5m 9427 0 50176 1 3584
100000 5m 9427 0 50176 1 3584
10000 40m 9427 0 50176 1 3584
25000 40m 9427 0 50176 1 3584
40000 40m 9427 0 50176 1 3584
50000 40m 9427 0 50176 1 3584
100000 40m 9427 0 50176 1 3584
10000 300m 9427 0 50176 1 3584
25000 300m 9427 0 50176 1 3584
40000 300m 9427 0 50176 1 3584
50000 300m 9427 0 50176 1 3584
100000 300m 9427 0 50176 1 3584
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# name lanes alias index speed
Ethernet0 62 tenGigE1/1 1 10000
Ethernet1 61 tenGigE1/2 2 10000
Ethernet2 64 tenGigE1/3 3 10000
Ethernet3 63 tenGigE1/4 4 10000
Ethernet4 66 tenGigE1/5 5 10000
Ethernet5 65 tenGigE1/6 6 10000
Ethernet6 68 tenGigE1/7 7 10000
Ethernet7 67 tenGigE1/8 8 10000
Ethernet8 70 tenGigE1/9 9 10000
Ethernet9 69 tenGigE1/10 10 10000
Ethernet10 72 tenGigE1/11 11 10000
Ethernet11 71 tenGigE1/12 12 10000
Ethernet12 74 tenGigE1/13 13 10000
Ethernet13 73 tenGigE1/14 14 10000
Ethernet14 76 tenGigE1/15 15 10000
Ethernet15 75 tenGigE1/16 16 10000
Ethernet16 78 tenGigE1/17 17 10000
Ethernet17 77 tenGigE1/18 18 10000
Ethernet18 80 tenGigE1/19 19 10000
Ethernet19 79 tenGigE1/20 20 10000
Ethernet20 3 tenGigE1/21 21 10000
Ethernet21 4 tenGigE1/22 22 10000
Ethernet22 1 tenGigE1/23 23 10000
Ethernet23 2 tenGigE1/24 24 10000
Ethernet24 7 tenGigE1/25 25 10000
Ethernet25 8 tenGigE1/26 26 10000
Ethernet26 5 tenGigE1/27 27 10000
Ethernet27 6 tenGigE1/28 28 10000
Ethernet28 11 tenGigE1/29 29 10000
Ethernet29 12 tenGigE1/30 30 10000
Ethernet30 9 tenGigE1/31 31 10000
Ethernet31 10 tenGigE1/32 32 10000
Ethernet32 15 tenGigE1/33 33 10000
Ethernet33 16 tenGigE1/34 34 10000
Ethernet34 13 tenGigE1/35 35 10000
Ethernet35 14 tenGigE1/36 36 10000
Ethernet36 19 tenGigE1/37 37 10000
Ethernet37 20 tenGigE1/38 38 10000
Ethernet38 17 tenGigE1/39 39 10000
Ethernet39 18 tenGigE1/40 40 10000
Ethernet40 23 tenGigE1/41 41 10000
Ethernet41 24 tenGigE1/42 42 10000
Ethernet42 21 tenGigE1/43 43 10000
Ethernet43 22 tenGigE1/44 44 10000
Ethernet44 27 tenGigE1/45 45 10000
Ethernet45 28 tenGigE1/46 46 10000
Ethernet46 25 tenGigE1/47 47 10000
Ethernet47 26 tenGigE1/48 48 10000
Ethernet48 40 twentyfiveGigE1/49 49 25000
Ethernet49 39 twentyfiveGigE1/50 50 25000
Ethernet50 38 twentyfiveGigE1/51 51 25000
Ethernet51 37 twentyfiveGigE1/52 52 25000
Ethernet52 41,42,43,44 hundredGigE1/53 53 100000
Ethernet56 45,46,47,48 hundredGigE1/54 54 100000
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{%- include 'qos_config_t1.j2' %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
{%- set PORT_ALL = [] %}
{%- for port in PORT %}
{%- if PORT_ALL.append(port) %}{% endif %}
{%- endfor %}
{%- if PORT_ALL | sort_by_port_index %}{% endif %}

{%- set port_names_list_all = [] %}
{%- for port in PORT_ALL %}
{%- if port_names_list_all.append(port) %}{% endif %}
{%- endfor %}
{%- set port_names_all = port_names_list_all | join(',') -%}


{%- set PORT_ACTIVE = [] %}
{%- if DEVICE_NEIGHBOR is not defined %}
{%- set PORT_ACTIVE = PORT_ALL %}
{%- else %}
{%- for port in DEVICE_NEIGHBOR.keys() %}
{%- if PORT_ACTIVE.append(port) %}{%- endif %}
{%- endfor %}
{%- endif %}
{%- if PORT_ACTIVE | sort_by_port_index %}{% endif %}

{%- set port_names_list_active = [] %}
{%- for port in PORT_ACTIVE %}
{%- if port_names_list_active.append(port) %}{%- endif %}
{%- endfor %}
{%- set port_names_active = port_names_list_active | join(',') -%}


{%- set pfc_to_pg_map_supported_asics = ['mellanox', 'barefoot', 'marvell'] -%}


{
{% if generate_tc_to_pg_map is defined %}
{{- generate_tc_to_pg_map() }}
{% else %}
"TC_TO_PRIORITY_GROUP_MAP": {
"AZURE": {
"0": "0",
"1": "0",
"2": "0",
"3": "3",
"4": "4",
"5": "0",
"6": "0",
"7": "7"
}
},
{% endif %}
"MAP_PFC_PRIORITY_TO_QUEUE": {
"AZURE": {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7"
}
},
"TC_TO_QUEUE_MAP": {
"AZURE": {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7"
}
},
"DSCP_TO_TC_MAP": {
"AZURE": {
"0" : "1",
"1" : "1",
"2" : "1",
"3" : "3",
"4" : "4",
"5" : "2",
"6" : "1",
"7" : "1",
"8" : "0",
"9" : "1",
"10": "1",
"11": "1",
"12": "1",
"13": "1",
"14": "1",
"15": "1",
"16": "1",
"17": "1",
"18": "1",
"19": "1",
"20": "1",
"21": "1",
"22": "1",
"23": "1",
"24": "1",
"25": "1",
"26": "1",
"27": "1",
"28": "1",
"29": "1",
"30": "1",
"31": "1",
"32": "1",
"33": "1",
"34": "1",
"35": "1",
"36": "1",
"37": "1",
"38": "1",
"39": "1",
"40": "1",
"41": "1",
"42": "1",
"43": "1",
"44": "1",
"45": "1",
"46": "5",
"47": "1",
"48": "6",
"49": "1",
"50": "1",
"51": "1",
"52": "1",
"53": "1",
"54": "1",
"55": "1",
"56": "1",
"57": "1",
"58": "1",
"59": "1",
"60": "1",
"61": "1",
"62": "1",
"63": "1"
}
},
"SCHEDULER": {
"scheduler.0": {
"type" : "DWRR",
"weight": "14"
},
"scheduler.1": {
"type" : "DWRR",
"weight": "15"
}
},
{% if asic_type in pfc_to_pg_map_supported_asics %}
"PFC_PRIORITY_TO_PRIORITY_GROUP_MAP": {
"AZURE": {
"3": "3",
"4": "4"
}
},
{% endif %}
"PORT_QOS_MAP": {
{% for port in PORT_ACTIVE %}
"{{ port }}": {
"dscp_to_tc_map" : "[DSCP_TO_TC_MAP|AZURE]",
"tc_to_queue_map" : "[TC_TO_QUEUE_MAP|AZURE]",
"tc_to_pg_map" : "[TC_TO_PRIORITY_GROUP_MAP|AZURE]",
"pfc_to_queue_map": "[MAP_PFC_PRIORITY_TO_QUEUE|AZURE]",
{% if asic_type in pfc_to_pg_map_supported_asics %}
"pfc_to_pg_map" : "[PFC_PRIORITY_TO_PRIORITY_GROUP_MAP|AZURE]",
{% endif %}
"pfc_enable" : "3,4"
}{% if not loop.last %},{% endif %}
{% endfor %}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/td3-x5-n3248pxe-48x10GCU+4x25G-2x100G.config.bcm
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
m0 load 0 0x0 /usr/share/sonic/hwsku/linkscan_led_fw.bin
m0 load 0 0x3800 /usr/share/sonic/hwsku/custom_led.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1,389 @@
pbmp_xport_xe=0x1FFFFFFFFFFFFFFE

# PM 4x25: CLP0
portmap_1=1:10
portmap_2=2:10
portmap_3=3:10
portmap_4=4:10

phy_chain_tx_polarity_flip_physical{1}=0x1
phy_chain_tx_polarity_flip_physical{2}=0x0
phy_chain_tx_polarity_flip_physical{3}=0x1
phy_chain_tx_polarity_flip_physical{4}=0x0

phy_chain_rx_polarity_flip_physical{1}=0x0
phy_chain_rx_polarity_flip_physical{2}=0x1
phy_chain_rx_polarity_flip_physical{3}=0x0
phy_chain_rx_polarity_flip_physical{4}=0x1

# PM 4x25: CLP1
portmap_5=5:10
portmap_6=6:10
portmap_7=7:10
portmap_8=8:10

phy_chain_tx_polarity_flip_physical{5}=0x0
phy_chain_tx_polarity_flip_physical{6}=0x1
phy_chain_tx_polarity_flip_physical{7}=0x0
phy_chain_tx_polarity_flip_physical{8}=0x1

phy_chain_rx_polarity_flip_physical{5}=0x1
phy_chain_rx_polarity_flip_physical{6}=0x0
phy_chain_rx_polarity_flip_physical{7}=0x1
phy_chain_rx_polarity_flip_physical{8}=0x0


# PM 4x25: CLP2
portmap_9=9:10
portmap_10=10:10
portmap_11=11:10
portmap_12=12:10

phy_chain_tx_polarity_flip_physical{9}=0x1
phy_chain_tx_polarity_flip_physical{10}=0x0
phy_chain_tx_polarity_flip_physical{11}=0x1
phy_chain_tx_polarity_flip_physical{12}=0x0

phy_chain_rx_polarity_flip_physical{9}=0x1
phy_chain_rx_polarity_flip_physical{10}=0x0
phy_chain_rx_polarity_flip_physical{11}=0x1
phy_chain_rx_polarity_flip_physical{12}=0x0



# PM 4x25: CLP3
portmap_13=13:10
portmap_14=14:10
portmap_15=15:10
portmap_16=16:10

phy_chain_tx_polarity_flip_physical{13}=0x0
phy_chain_tx_polarity_flip_physical{14}=0x1
phy_chain_tx_polarity_flip_physical{15}=0x0
phy_chain_tx_polarity_flip_physical{16}=0x1

phy_chain_rx_polarity_flip_physical{13}=0x1
phy_chain_rx_polarity_flip_physical{14}=0x0
phy_chain_rx_polarity_flip_physical{15}=0x1
phy_chain_rx_polarity_flip_physical{16}=0x0


# PM 4x25: CLP4
portmap_17=17:10
portmap_18=18:10
portmap_19=19:10
portmap_20=20:10

phy_chain_tx_polarity_flip_physical{17}=0x1
phy_chain_tx_polarity_flip_physical{18}=0x0
phy_chain_tx_polarity_flip_physical{19}=0x1
phy_chain_tx_polarity_flip_physical{20}=0x0

phy_chain_rx_polarity_flip_physical{17}=0x0
phy_chain_rx_polarity_flip_physical{18}=0x1
phy_chain_rx_polarity_flip_physical{19}=0x0
phy_chain_rx_polarity_flip_physical{20}=0x1

# PM 4x25: CLP5
portmap_21=21:10
portmap_22=22:10
portmap_23=23:10
portmap_24=24:10

phy_chain_tx_polarity_flip_physical{21}=0x0
phy_chain_tx_polarity_flip_physical{22}=0x1
phy_chain_tx_polarity_flip_physical{23}=0x0
phy_chain_tx_polarity_flip_physical{24}=0x1

phy_chain_rx_polarity_flip_physical{21}=0x1
phy_chain_rx_polarity_flip_physical{22}=0x0
phy_chain_rx_polarity_flip_physical{23}=0x1
phy_chain_rx_polarity_flip_physical{24}=0x0


# PM 4x25: CLP6
portmap_25=25:10
portmap_26=26:10
portmap_27=27:10
portmap_28=28:10

phy_chain_tx_polarity_flip_physical{25}=0x1
phy_chain_tx_polarity_flip_physical{26}=0x0
phy_chain_tx_polarity_flip_physical{27}=0x1
phy_chain_tx_polarity_flip_physical{28}=0x0

phy_chain_rx_polarity_flip_physical{25}=0x1
phy_chain_rx_polarity_flip_physical{26}=0x0
phy_chain_rx_polarity_flip_physical{27}=0x1
phy_chain_rx_polarity_flip_physical{28}=0x0




# PM 4x25: CLP9 4x25G ports
portmap_29=37:25
portmap_30=38:25
portmap_31=39:25
portmap_32=40:25

phy_chain_tx_polarity_flip_physical{37}=0x1
phy_chain_tx_polarity_flip_physical{38}=0x0
phy_chain_tx_polarity_flip_physical{39}=0x1
phy_chain_tx_polarity_flip_physical{40}=0x1

phy_chain_rx_polarity_flip_physical{37}=0x1
phy_chain_rx_polarity_flip_physical{38}=0x0
phy_chain_rx_polarity_flip_physical{39}=0x1
phy_chain_rx_polarity_flip_physical{40}=0x1



# PM 4x25: CLP15
portmap_33=61:10
portmap_34=62:10
portmap_35=63:10
portmap_36=64:10

phy_chain_tx_polarity_flip_physical{61}=0x0
phy_chain_tx_polarity_flip_physical{62}=0x1
phy_chain_tx_polarity_flip_physical{63}=0x0
phy_chain_tx_polarity_flip_physical{64}=0x1

phy_chain_rx_polarity_flip_physical{61}=0x0
phy_chain_rx_polarity_flip_physical{62}=0x1
phy_chain_rx_polarity_flip_physical{63}=0x0
phy_chain_rx_polarity_flip_physical{64}=0x1

# PM 4x25: CLP16
portmap_37=65:10
portmap_38=66:10
portmap_39=67:10
portmap_40=68:10

phy_chain_tx_polarity_flip_physical{65}=0x1
phy_chain_tx_polarity_flip_physical{66}=0x0
phy_chain_tx_polarity_flip_physical{67}=0x1
phy_chain_tx_polarity_flip_physical{68}=0x0

phy_chain_rx_polarity_flip_physical{65}=0x1
phy_chain_rx_polarity_flip_physical{66}=0x0
phy_chain_rx_polarity_flip_physical{67}=0x1
phy_chain_rx_polarity_flip_physical{68}=0x0


# PM 4x25: CLP17
portmap_41=69:10
portmap_42=70:10
portmap_43=71:10
portmap_44=72:10

phy_chain_tx_polarity_flip_physical{69}=0x0
phy_chain_tx_polarity_flip_physical{70}=0x1
phy_chain_tx_polarity_flip_physical{71}=0x0
phy_chain_tx_polarity_flip_physical{72}=0x1

phy_chain_rx_polarity_flip_physical{69}=0x1
phy_chain_rx_polarity_flip_physical{70}=0x0
phy_chain_rx_polarity_flip_physical{71}=0x1
phy_chain_rx_polarity_flip_physical{72}=0x0

# PM 4x25: CLP18
portmap_45=73:10
portmap_46=74:10
portmap_47=75:10
portmap_48=76:10

phy_chain_tx_polarity_flip_physical{73}=0x1
phy_chain_tx_polarity_flip_physical{74}=0x0
phy_chain_tx_polarity_flip_physical{75}=0x1
phy_chain_tx_polarity_flip_physical{76}=0x0

phy_chain_rx_polarity_flip_physical{73}=0x1
phy_chain_rx_polarity_flip_physical{74}=0x0
phy_chain_rx_polarity_flip_physical{75}=0x1
phy_chain_rx_polarity_flip_physical{76}=0x0



# PM 4x25: CLP19
portmap_49=77:10
portmap_50=78:10
portmap_51=79:10
portmap_52=80:10

phy_chain_tx_polarity_flip_physical{77}=0x0
phy_chain_tx_polarity_flip_physical{78}=0x1
phy_chain_tx_polarity_flip_physical{79}=0x0
phy_chain_tx_polarity_flip_physical{80}=0x1

phy_chain_rx_polarity_flip_physical{77}=0x0
phy_chain_rx_polarity_flip_physical{78}=0x1
phy_chain_rx_polarity_flip_physical{79}=0x0
phy_chain_rx_polarity_flip_physical{80}=0x1

portmap_53=41:100
phy_chain_tx_lane_map_physical{41.0}=0x2130
phy_chain_rx_lane_map_physical{41.0}=0x0312


phy_chain_tx_polarity_flip_physical{41.0}=0x0
phy_chain_tx_polarity_flip_physical{42.0}=0x1
phy_chain_tx_polarity_flip_physical{43.0}=0x1
phy_chain_tx_polarity_flip_physical{44.0}=0x0

phy_chain_rx_polarity_flip_physical{41.0}=0x0
phy_chain_rx_polarity_flip_physical{42.0}=0x1
phy_chain_rx_polarity_flip_physical{43.0}=0x1
phy_chain_rx_polarity_flip_physical{44.0}=0x0

# PM 4x25: CLP11
portmap_57=45:100
phy_chain_tx_lane_map_physical{45.0}=0x2130
phy_chain_rx_lane_map_physical{45.0}=0x0312


phy_chain_tx_polarity_flip_physical{45.0}=0x0
phy_chain_tx_polarity_flip_physical{46.0}=0x1
phy_chain_tx_polarity_flip_physical{47.0}=0x1
phy_chain_tx_polarity_flip_physical{48.0}=0x0

phy_chain_rx_polarity_flip_physical{45.0}=0x0
phy_chain_rx_polarity_flip_physical{46.0}=0x1
phy_chain_rx_polarity_flip_physical{47.0}=0x1
phy_chain_rx_polarity_flip_physical{48.0}=0x0


dport_map_port_34=1
dport_map_port_33=2
dport_map_port_36=3
dport_map_port_35=4
dport_map_port_38=5
dport_map_port_37=6
dport_map_port_40=7
dport_map_port_39=8
dport_map_port_42=9
dport_map_port_41=10
dport_map_port_44=11
dport_map_port_43=12
dport_map_port_46=13
dport_map_port_45=14
dport_map_port_48=15
dport_map_port_47=16
dport_map_port_50=17
dport_map_port_49=18
dport_map_port_52=19
dport_map_port_51=20
dport_map_port_3=21
dport_map_port_4=22
dport_map_port_1=23
dport_map_port_2=24
dport_map_port_7=25
dport_map_port_8=26
dport_map_port_5=27
dport_map_port_6=28
dport_map_port_11=29
dport_map_port_12=30
dport_map_port_9=31
dport_map_port_10=32
dport_map_port_15=33
dport_map_port_16=34
dport_map_port_13=35
dport_map_port_14=36
dport_map_port_19=37
dport_map_port_20=38
dport_map_port_17=39
dport_map_port_18=40
dport_map_port_23=41
dport_map_port_24=42
dport_map_port_21=43
dport_map_port_22=44
dport_map_port_27=45
dport_map_port_28=46
dport_map_port_25=47
dport_map_port_26=48
dport_map_port_32=49
dport_map_port_31=50
dport_map_port_30=51
dport_map_port_29=52
dport_map_port_53=53
dport_map_port_54=54
dport_map_port_55=55
dport_map_port_56=56
dport_map_port_57=57
dport_map_port_58=58
dport_map_port_59=59
dport_map_port_60=60



#pbmp_oversubscribe=0x7fff9fffffffffffffffe
#pbmp_xport_xe=0x7fff9fffffffffffffffe
port_flex_enable=1
phy_an_c73=3
oversubscribe_mode=1
core_clock_frequency=1525

#25G,10G and 1G support
serdes_10g_at_25g_vco=1
serdes_1000x_at_25g_vco=1

l2xmsg_mode=1

l2xmsg_hostbuf_size=16384
module_64ports=0

#Interrupts and Parity
max_vp_lags=0

schan_intr_enable=0
tdma_timeout_usec=5000000

stable_size=0x5500000

#Default L3 profile
l2_mem_entries=40960
l3_alpm_enable=2
l3_alpm_ipv6_128b_bkt_rsvd=1
l3_mem_entries=40960

#Tunnels
use_all_splithorizon_groups=1
sai_tunnel_support=1
bcm_tunnel_term_compatible_mode=1

#RIOT Enable
riot_enable=1
riot_overlay_l3_intf_mem_size=8192
riot_overlay_l3_egress_mem_size=32768
l3_ecmp_levels=2
riot_overlay_ecmp_resilient_hash_size=16384

#sai_preinit_cmd_file=/usr/share/sonic/hwsku/sai_preinit_cmd.soc

#New Additions
pfc_deadlock_seq_control=1

#Common configs from broadcom/x86_64-broadcom_common/x86_64-broadcom_b77/broadcom-sonic-td3.config.bcm (Lower version of Td3 (0xb771))
mem_cache_enable=0
ifp_inports_support_enable=1
ipv6_lpm_128b_enable=0x1
l3_max_ecmp_mode=1
lpm_scaling_enable=0
bcm_num_cos=10
default_cpu_tx_queue=9
mmu_lossless=0
host_as_route_disable=1
sai_eapp_config_file=/etc/broadcom/eapps_cfg.json
sai_fast_convergence_support=1
flow_init_mode=1
sai_interface_type_auto_detect=0
mpls_mem_entries=16384
vlan_xlate_1_mem_entries=65536
vlan_xlate_2_mem_entries=16384
sai_load_hw_config=/usr/lib/cancun/
sai_nbr_bcast_ifp_optimized=1
sai_brcm_sonic_acl_enhancements=1
# Reduced Trap Group QSET for BRCM Sonic
sai_brcm_sonic_trap_group=1
l2_entry_used_as_my_station=1
multi_hash_recurse_depth_l3=2
1 change: 1 addition & 0 deletions device/dell/x86_64-dellemc_n3248pxe_c3338-r0/default_sku
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DELLEMC-N3248PXE t1
3 changes: 3 additions & 0 deletions device/dell/x86_64-dellemc_n3248pxe_c3338-r0/installer.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CONSOLE_PORT=0x3f8
CONSOLE_DEV=0
VAR_LOG_SIZE=512
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# LED microprocessor initialization for Dell N3248TE
#
#
#Led0
#led auto on
m0 load 0 0x3800 /usr/share/sonic/hwsku/custom_led.bin
led start
22 changes: 22 additions & 0 deletions device/dell/x86_64-dellemc_n3248pxe_c3338-r0/plugins/eeprom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/python3

#############################################################################
# Dell S3000
#
# Platform and model specific eeprom subclass, inherits from the base class,
# and provides the followings:
# - the eeprom format definition
# - specific encoder/decoder if there is special need
#############################################################################

try:
from sonic_eeprom import eeprom_tlvinfo
except ImportError as e:
raise ImportError (str(e) + "- required module not found")


class board(eeprom_tlvinfo.TlvInfoDecoder):

def __init__(self, name, path, cpld_root, ro):
self.eeprom_path = "/sys/class/i2c-adapter/i2c-2/2-0050/eeprom"
super(board, self).__init__(self.eeprom_path, 0, '', True)
72 changes: 72 additions & 0 deletions device/dell/x86_64-dellemc_n3248pxe_c3338-r0/plugins/fanutil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#
# fanutil.py
# Platform-specific FAN status interface for SONiC
#

import commands
import sys

SENSORS_CMD = "docker exec -i pmon /usr/bin/sensors"
DOCKER_SENSORS_CMD = "/usr/bin/sensors"


try:
from sonic_fan.fan_base import FanBase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")


class FanUtil(FanBase):
"""Platform-specific FanUtil class"""
_fan_mapping = {
1 : '0',
2 : '1',
3 : '2'
}

def __init__(self):
FanBase.__init__(self)

def isDockerEnv(self):
num_docker = open('/proc/self/cgroup', 'r').read().count(":/docker")
if num_docker > 0:
return True

def get_num_fans(self):
n3248pxe_MAX_FANTRAYS = 3
return n3248pxe_MAX_FANTRAYS

def get_presence(self, idx):
sysfs_path = "/sys/devices/platform/dell-n3248pxe-cpld.0/fan" + self._fan_mapping[idx] + "_prs"
return int(open(sysfs_path).read(), 16)

def get_direction(self, idx):
sysfs_path = "/sys/devices/platform/dell-n3248pxe-cpld.0/fan" + self._fan_mapping[idx] + "_dir"
return open(sysfs_path).read()

def get_speed(self, idx):
dockerenv = self.isDockerEnv()
if not dockerenv:
status, cmd_output = commands.getstatusoutput(SENSORS_CMD)
else :
status, cmd_output = commands.getstatusoutput(DOCKER_SENSORS_CMD)

if status:
print('Failed to execute sensors command')
sys.exit(0)
fan_id = 'Fan ' + str(idx)
found = False
for line in cmd_output.splitlines():
if line.startswith('emc2305-i2c-7-2c'):
found = True
if found and line.startswith(fan_id):
return line.split()[3]
return 0.0

def get_status(self, idx):
sysfs_path = "/sys/devices/platform/dell-n3248pxe-cpld.0/fan" + self._fan_mapping[idx] + "_prs"
return int(open(sysfs_path).read(), 16)


def set_speed(self, idx):
return False
191 changes: 191 additions & 0 deletions device/dell/x86_64-dellemc_n3248pxe_c3338-r0/plugins/psuutil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#
# psuutil.py
# Platform-specific PSU status interface for SONiC
#

import commands
import os
import sys

SENSORS_CMD = "docker exec -i pmon /usr/bin/sensors"
DOCKER_SENSORS_CMD = "/usr/bin/sensors"

try:
from sonic_psu.psu_base import PsuBase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")


class PsuUtil(PsuBase):
"""Platform-specific PSUutil class"""
_psu_mapping = {
1 : '0',
2 : '1'
}

def __init__(self):
PsuBase.__init__(self)

def isDockerEnv(self):
num_docker = open('/proc/self/cgroup', 'r').read().count(":/docker")
if num_docker > 0:
return True
else:
return False

def remove_nonnumeric(self, text):
digits='0123456789.'
return ''.join(c for c in text if c in digits)

def get_cpld_register(self, reg_name):
cpld_dir = "/sys/devices/platform/dell-n3248pxe-cpld.0/"
retval = 'ERR'
reg_file = cpld_dir +'/' + reg_name
if (not os.path.isfile(reg_file)):
return retval

try:
with open(reg_file, 'r') as fd:
retval = fd.read()
except Exception:
print("Unable to open ", reg_file, "file !")

retval = retval.rstrip('\r\n')
return retval

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
"""
N3248PXE_MAX_PSUS = 2
return N3248PXE_MAX_PSUS

def get_psu_status(self, index):
"""
Retrieves the oprational status of power supply unit (PSU) defined
by index <index>
:param index: An integer, index of the PSU of which to query status
:return: Boolean, True if PSU is operating properly, False if PSU is\
faulty
"""
status = 0
psu_status = self.get_cpld_register('psu'+self._psu_mapping[index]+'_status')
if (psu_status != 'ERR'):
status = int(psu_status, 10)

presence = self.get_psu_presence(index)

return (status & presence)

def get_psu_presence(self, index):
"""
Retrieves the presence status of power supply unit (PSU) defined
by index <index>
:param index: An integer, index of the PSU of which to query status
:return: Boolean, True if PSU is plugged, False if not
"""
status = 0
psu_presence = self.get_cpld_register('psu'+self._psu_mapping[index]+'_prs')
if (psu_presence != 'ERR'):
status = int(psu_presence, 10)

return status

def get_sensor(self):
dockerenv = self.isDockerEnv()
if not dockerenv:
status, cmd_output = commands.getstatusoutput(SENSORS_CMD)
else :
status, cmd_output = commands.getstatusoutput(DOCKER_SENSORS_CMD)

if status:
print('Failed to execute sensors command')
sys.exit(0)
return cmd_output

def get_output_current(self, index):
cmd_output= self.get_sensor()
sensor_name = 'dps460-i2c-10' if index == 1 else 'dps460-i2c-11'
found = False
for line in cmd_output.splitlines():
if line.startswith(sensor_name):
found = True
if found:
if 'Output Current' in line :
return float(self.remove_nonnumeric(line.split()[2]))
return 0.0

def get_output_voltage(self, index):
cmd_output= self.get_sensor()
sensor_name = 'dps460-i2c-10' if index == 1 else 'dps460-i2c-11'
found = False
for line in cmd_output.splitlines():
if line.startswith(sensor_name):
found = True
if found:
if 'Output Voltage' in line :
return float(self.remove_nonnumeric(line.split()[2]))
return 0.0

def get_fan_rpm(self, index, fan_index):
if fan_index > 1 : return 0.0
cmd_output= self.get_sensor()
sensor_name = 'dps460-i2c-10' if index == 1 else 'dps460-i2c-11'
found = False
for line in cmd_output.splitlines():
if line.startswith(sensor_name):
found = True
if found:
if 'Fan RPM' in line :
return self.remove_nonnumeric(line.split()[2])
return 0.0

def get_output_power(self, index):
cmd_output= self.get_sensor()
sensor_name = 'dps460-i2c-10' if index == 1 else 'dps460-i2c-11'
found = False
for line in cmd_output.splitlines():
if line.startswith(sensor_name):
found = True
if found:
if 'Output Power' in line :
return float(self.remove_nonnumeric(line.split()[2]))
return 0.0

def get_direction(self, index):
psuid = '0' if index == 1 else '1'
sysfs_path = '/sys/devices/platform/dell-n3248pxe-cpld.0/psu' + psuid + '_prs'
found_psu = int(open(sysfs_path).read())
if not found_psu : return ''
bus_no = '10' if index == 1 else '11'
sysfs_path = "/sys/bus/i2c/devices/" + bus_no + "-0056/eeprom"
val = (open(sysfs_path, "rb").read())[0xe1:0xe8]
dir = 'F2B' if 'FORWARD' == val else 'B2F'
return dir

def get_serial(self, index):
psuid = '0' if index == 1 else '1'
sysfs_path = '/sys/devices/platform/dell-n3248pxe-cpld.0/psu' + psuid + '_prs'
found_psu = int(open(sysfs_path).read())
if not found_psu : return ''
bus_no = '10' if index == 1 else '11'
sysfs_path = "/sys/bus/i2c/devices/" + bus_no + "-0056/eeprom"
val = (open(sysfs_path, "rb").read())[0xc4:0xd9]
return val

def get_model(self, index):
psuid = '0' if index == 1 else '1'
sysfs_path = '/sys/devices/platform/dell-n3248pxe-cpld.0/psu' + psuid + '_prs'
found_psu = int(open(sysfs_path).read())
if not found_psu : return ''
bus_no = '10' if index == 1 else '11'
sysfs_path = "/sys/bus/i2c/devices/" + bus_no + "-0056/eeprom"
val = (open(sysfs_path, "rb").read())[0x50:0x62]
return val

def get_mfr_id(self, index):
psuid = '0' if index == 1 else '1'
sysfs_path = '/sys/devices/platform/dell-n3248pxe-cpld.0/psu' + psuid + '_prs'
found_psu = int(open(sysfs_path).read())
return 'DELTA' if found_psu else ''
172 changes: 172 additions & 0 deletions device/dell/x86_64-dellemc_n3248pxe_c3338-r0/plugins/sfputil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# sfputil.py
#
# Platform-specific SFP transceiver interface for SONiC
#

try:
import time
from socket import *
from select import *
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 = 52
PORTS_IN_BLOCK = 52
SFP_PORT_START = 49
SFP_PORT_END = 52

EEPROM_OFFSET = 14

_port_to_eeprom_mapping = {}
_sfpp_port_i2c_mapping = {
49 : 20,
50 : 21,
51 : 22,
52 : 23
}
port_dict = {}

@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.SFP_PORT_END+1, self.SFP_PORT_END+1)

@property
def port_to_eeprom_mapping(self):
return self._port_to_eeprom_mapping

@property
def get_transceiver_status(self):

try:
sfp_modprs_path = "/sys/devices/platform/dell-n3248pxe-cpld.0/sfp_modprs"
reg_file = open(sfp_modprs_path)

except IOError as e:
print ("Error: unable to open file: %s" % str(e))
return False

content = reg_file.readline().rstrip()

reg_file.close()

return int(content, 16)


def __init__(self):

sfpplus_eeprom_path = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom"

for x in range(self.SFP_PORT_START, self.SFP_PORT_END + 1):
self.port_to_eeprom_mapping[x] = sfpplus_eeprom_path.format(self._sfpp_port_i2c_mapping[x])
# Get Transceiver status
self.modprs_register = self.get_transceiver_status

SfpUtilBase.__init__(self)

def get_presence(self, port_num):
# Check for invalid port_num
if port_num < self.port_start or port_num > self.port_end:
return False

if port_num < self.SFP_PORT_START :
return False
port_num -= self.SFP_PORT_START
try:
sfp_modprs_path = "/sys/devices/platform/dell-n3248pxe-cpld.0/sfp_modprs"
reg_file = open(sfp_modprs_path)
except IOError as e:
print ("Error: unable to open file: %s" % str(e))
return False

content = reg_file.readline().rstrip()

# content is a string containing the hex representation of the register
reg_value = int(content, 16)

# Mask off the bit corresponding to our port
mask = (1 << port_num)

# ModPrsL is active low
if (reg_value & mask) == 0:
return True

return False

def get_low_power_mode(self, port_num):
return False

def set_low_power_mode(self, port_num, lpmode):
return False

def reset(self, port_num):
return False

def get_transceiver_change_event(self, timeout=0):

start_time = time.time()
port = self.SFP_PORT_START
forever = False

if timeout == 0:
forever = True
elif timeout > 0:
timeout = timeout / float(1000) # Convert to secs
else:
print ('get_transceiver_change_event:Invalid timeout value', timeout)
return False, {}

end_time = start_time + timeout
if start_time > end_time:
print ('get_transceiver_change_event:' \
'time wrap / invalid timeout value', timeout)

return False, {} # Time wrap or possibly incorrect timeout

while timeout >= 0:
# Check for OIR events and return updated port_dict
reg_value = self.get_transceiver_status
if reg_value != self.modprs_register:
changed_ports = self.modprs_register ^ reg_value
while port >= self.SFP_PORT_START and port <= self.SFP_PORT_END:

# Mask off the bit corresponding to our port
mask = (1 << (port - self.SFP_PORT_START))

if changed_ports & mask:
# ModPrsL is active low
if reg_value & mask == 0:
self.port_dict[port] = '1'
else:
self.port_dict[port] = '0'

port += 1

# Update reg value
self.modprs_register = reg_value
return True, self.port_dict

if forever:
time.sleep(1)
else:
timeout = end_time - time.time()
if timeout >= 1:
time.sleep(1) # We poll at 1 second granularity
else:
if timeout > 0:
time.sleep(timeout)
return True, {}
print ("get_transceiver_change_event: Should not reach here.")
return False, {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"skip_ledd": true
}
58 changes: 58 additions & 0 deletions device/dell/x86_64-dellemc_n3248pxe_c3338-r0/sensors.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# libsensors configuration file for Dell N3248TE
# The i2c bus portion is omit because adapter name
# changes every time when system boot up.

bus "i2c-7" "i2c-0-mux (chan_id 5)"
bus "i2c-5" "i2c-0-mux (chan_id 3)"
bus "i2c-10" "i2c-5-mux (chan_id 0)"
bus "i2c-11" "i2c-5-mux (chan_id 1)"

chip "tmp75-i2c-7-49"
label temp1 "Switch Near Temperature"
chip "tmp75-i2c-7-4a"
label temp1 "Switch Rear Temperature"
chip "tmp75-i2c-7-4b"
label temp1 "Front Panel PHY Temperature"
chip "tmp75-i2c-7-4c"
label temp1 "Near Front Panel Temperature"
chip "tmp75-i2c-7-4f"
label temp1 "Middle Fan Tray Temperature"


chip "emc2305-i2c-7-2c"
ignore fan4
ignore fan5
label fan1 "Fan 1 "
label fan2 "Fan 2 "
label fan3 "Fan 3 "

chip "dps460-i2c-10-5e"
label power1 "Input Power"
label power2 "Output Power"
label curr1 "Input Current"
label curr2 "Output Current"
label in1 "Input Voltage"
ignore in2
label in3 "Output Voltage"
label fan1 "Fan RPM"
ignore fan2
ignore fan3
ignore temp1
label temp2 "FAN Airflow Temperature"
label temp3 "FAN Normal Temperature"


chip "dps460-i2c-11-5e"
label power1 "Input Power"
label power2 "Output Power"
label curr1 "Input Current"
label curr2 "Output Current"
label in1 "Input Voltage"
ignore in2
label in3 "Output Voltage"
label fan1 "Fan RPM"
ignore fan2
ignore fan3
ignore temp1
label temp2 "FAN Airflow Temperature"
label temp3 "FAN Normal Temperature"
1 change: 1 addition & 0 deletions platform/broadcom/one-image.mk
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \
$(DELL_S5296F_PLATFORM_MODULE) \
$(DELL_Z9100_PLATFORM_MODULE) \
$(DELL_S6100_PLATFORM_MODULE) \
$(DELL_N3248PXE_PLATFORM_MODULE) \
$(DELL_N3248TE_PLATFORM_MODULE) \
$(INGRASYS_S8900_54XC_PLATFORM_MODULE) \
$(INGRASYS_S8900_64XC_PLATFORM_MODULE) \
6 changes: 6 additions & 0 deletions platform/broadcom/platform-modules-dell.mk
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ DELL_S5232F_PLATFORM_MODULE_VERSION = 1.1
DELL_Z9332F_PLATFORM_MODULE_VERSION = 1.1
DELL_S5248F_PLATFORM_MODULE_VERSION = 1.1
DELL_S5296F_PLATFORM_MODULE_VERSION = 1.1
DELL_N3248PXE_PLATFORM_MODULE_VERSION = 1.1
DELL_N3248TE_PLATFORM_MODULE_VERSION = 1.1

export DELL_S6000_PLATFORM_MODULE_VERSION
@@ -18,6 +19,7 @@ export DELL_S5232F_PLATFORM_MODULE_VERSION
export DELL_Z9332F_PLATFORM_MODULE_VERSION
export DELL_S5248F_PLATFORM_MODULE_VERSION
export DELL_S5296F_PLATFORM_MODULE_VERSION
export DELL_N3248PXE_PLATFORM_MODULE_VERSION
export DELL_N3248TE_PLATFORM_MODULE_VERSION

DELL_Z9100_PLATFORM_MODULE = platform-modules-z9100_$(DELL_Z9100_PLATFORM_MODULE_VERSION)_amd64.deb
@@ -58,3 +60,7 @@ $(eval $(call add_extra_package,$(DELL_Z9100_PLATFORM_MODULE),$(DELL_N3248TE_PLA
DELL_S5296F_PLATFORM_MODULE = platform-modules-s5296f_$(DELL_S5296F_PLATFORM_MODULE_VERSION)_amd64.deb
$(DELL_S5296F_PLATFORM_MODULE)_PLATFORM = x86_64-dellemc_s5296f_c3538-r0
$(eval $(call add_extra_package,$(DELL_Z9100_PLATFORM_MODULE),$(DELL_S5296F_PLATFORM_MODULE)))

DELL_N3248PXE_PLATFORM_MODULE = platform-modules-n3248pxe_$(DELL_N3248PXE_PLATFORM_MODULE_VERSION)_amd64.deb
$(DELL_N3248PXE_PLATFORM_MODULE)_PLATFORM = x86_64-dellemc_n3248pxe_c3338-r0
$(eval $(call add_extra_package,$(DELL_Z9100_PLATFORM_MODULE),$(DELL_N3248PXE_PLATFORM_MODULE)))
5 changes: 5 additions & 0 deletions platform/broadcom/sonic-platform-modules-dell/debian/control
Original file line number Diff line number Diff line change
@@ -45,6 +45,11 @@ Architecture: amd64
Depends: linux-image-4.19.0-12-2-amd64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp

Package: platform-modules-n3248pxe
Architecture: amd64
Depends: linux-image-4.19.0-12-2-amd64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp

Package: platform-modules-s5296f
Architecture: amd64
Depends: linux-image-4.9.0-9-2-amd64
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/bash

### BEGIN INIT INFO
# Provides: setup-board
# Required-Start:
# Required-Stop:
# Should-Start:
# Should-Stop:
# Default-Start: S
# Default-Stop: 0 6
# Short-Description: Setup N32xx board.
### END INIT INFO

case "$1" in
start)
echo -n "Setting up board... "

/usr/local/bin/n3248pxe_platform.sh init

echo "done."
;;

stop)
/usr/local/bin/n3248pxe_platform.sh deinit
echo "done."

;;

force-reload|restart)
echo "Not supported"
;;

*)
echo "Usage: /etc/init.d/platform-modules-n3248pxe.init {start|stop}"
exit 1
;;
esac

exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
n3248pxe/scripts/n3248pxe_platform.sh usr/local/bin
n3248pxe/scripts/platform_sensors.py usr/local/bin
n3248pxe/scripts/sensors usr/bin
n3248pxe/scripts//portiocfg.py usr/local/bin
n3248pxe/scripts//ports_xcvrd_notify.py usr/local/bin
n3248pxe/systemd/platform-modules-n3248pxe.service etc/systemd/system
n3248pxe/cfg/n3248pxe-modules.conf etc/modules-load.d
common/dell_i2c_utils.sh usr/local/bin
n3248pxe/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/x86_64-dellemc_n3248pxe_c3338-r0
common/platform_reboot usr/share/sonic/device/x86_64-dellemc_n3248pxe_c3338-r0
common/fw-updater usr/local/bin
common/onie_mode_set usr/local/bin
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# postinst script for n3248pxe

# Enable Dell-n3248pxe-platform-service
depmod -a
systemctl enable platform-modules-n3248pxe.service
systemctl start platform-modules-n3248pxe.service
#DEBHELPER#
13 changes: 12 additions & 1 deletion platform/broadcom/sonic-platform-modules-dell/debian/rules
Original file line number Diff line number Diff line change
@@ -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:= s6000 z9100 s6100 z9264f s5232f s5248f z9332f s5296f n3248te
MODULE_DIRS:= s6000 z9100 s6100 z9264f s5232f s5248f z9332f s5296f n3248pxe n3248te
COMMON_DIR := common

%:
@@ -61,6 +61,12 @@ override_dh_auto_build:
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
cd $(MOD_SRC_DIR); \
elif [ $$mod = "n3248pxe" ]; then \
cp $(COMMON_DIR)/ipmihelper.py $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
cd $(MOD_SRC_DIR)/$${mod}; \
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
cd $(MOD_SRC_DIR); \
fi; \
echo "making man page alias $$mod -> $$mod APIs";\
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \
@@ -123,6 +129,11 @@ override_dh_clean:
rm -f $(MOD_SRC_DIR)/$${mod}/modules/*.whl; \
rm -rf $(MOD_SRC_DIR)/$${mod}/build; \
rm -rf $(MOD_SRC_DIR)/$${mod}/build/*.egg-info; \
elif [ $$mod = "n3248pxe" ]; then \
rm -f $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
rm -f $(MOD_SRC_DIR)/$${mod}/modules/*.whl; \
rm -rf $(MOD_SRC_DIR)/$${mod}/build; \
rm -rf $(MOD_SRC_DIR)/$${mod}/build/*.egg-info; \
elif [ $$mod = "n3248te" ]; then \
rm -f $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
rm -f $(MOD_SRC_DIR)/$${mod}/modules/*.whl; \
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# /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-isch
i2c-ismt
i2c-dev
i2c-mux
i2c-smbus

i2c-mux-pca954x
dell_n3248pxe_platform

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
obj-m := dell_n3248pxe_platform.o emc2305.o

Large diffs are not rendered by default.

Large diffs are not rendered by default.

425 changes: 425 additions & 0 deletions platform/broadcom/sonic-platform-modules-dell/n3248pxe/modules/pmbus.h

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#! /bin/sh

### BEGIN INIT INFO
# Provides: fancontrol
# Required-Start: $remote_fs
# Required-Stop: $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: fancontrol
# Description: fan speed regulator
### END INIT INFO

. /lib/lsb/init-functions

[ -f /etc/default/rcS ] && . /etc/default/rcS
PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin
DAEMON=/usr/local/bin/fancontrol
DESC="fan speed regulator"
NAME="fancontrol"
PIDFILE=/var/run/fancontrol.pid
PLATFORMPATH=/sys/devices/platform/LPC
MAIN_CONF=/usr/share/sonic/device/x86_64-dell_s3000_c2338-r0/fancontrol
DEVPATH=/sys/devices/pci0000:00/0000:00:13.0/i2c-0/i2c-8/i2c-23/23-004d

test -x $DAEMON || exit 0

for i in 1 2 3
do
j=$i
[ $i -eq 3 ] && j=4
FANFAULT=$(cat ${DEVPATH}/fan${j}_fault)
[ $FANFAULT = 1 ] && continue
FANDIR=$(cat ${PLATFORMPATH}/fan${i}_dir)
done
CONF=${MAIN_CONF}-${FANDIR}

case "$1" in
start)
if [ -f $CONF ] ; then
if $DAEMON --check $CONF 1>/dev/null 2>/dev/null ; then
log_daemon_msg "Starting $DESC" "$NAME\n"
start-stop-daemon --start --quiet --pidfile $PIDFILE --startas $DAEMON $CONF
log_end_msg $?
else
log_failure_msg "Not starting fancontrol, broken configuration file; please re-run pwmconfig."
fi
else
if [ "$VERBOSE" != no ]; then
log_warning_msg "Not starting fancontrol; run pwmconfig first."
fi
fi
;;
stop)
log_daemon_msg "Stopping $DESC" "$NAME"
start-stop-daemon --stop --quiet --pidfile $PIDFILE --oknodo --startas $DAEMON $CONF
rm -f $PIDFILE
log_end_msg $?
;;
restart)
$0 stop
sleep 3
$0 start
;;
force-reload)
if start-stop-daemon --stop --test --quiet --pidfile $PIDFILE --startas $DAEMON $CONF ; then
$0 restart
fi
;;
status)
status_of_proc $DAEMON $NAME $CONF && exit 0 || exit $?
;;
*)
log_success_msg "Usage: /etc/init.d/fancontrol {start|stop|restart|force-reload|status}"
exit 1
;;
esac

exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#!/bin/bash

#platform init script for Dell n3248pxe

source dell_i2c_utils.sh

#Attach/Detach the system devices
sys_devices() {
case $1 in
"new_device") #syseeprom
i2c_config "echo 24c02 0x50 > /sys/bus/i2c/devices/i2c-2/$1"
#Attach Fan Controller
i2c_config "echo emc2305 0x2c > /sys/bus/i2c/devices/i2c-7/$1"
#Attach temperature monitor
i2c_config "echo tmp75 0x49 > /sys/bus/i2c/devices/i2c-7/$1"
i2c_config "echo tmp75 0x4a > /sys/bus/i2c/devices/i2c-7/$1"
i2c_config "echo tmp75 0x4b > /sys/bus/i2c/devices/i2c-7/$1"
i2c_config "echo tmp75 0x4c > /sys/bus/i2c/devices/i2c-7/$1"
i2c_config "echo tmp75 0x4f > /sys/bus/i2c/devices/i2c-7/$1"
#Attach PSU Controller
i2c_config "echo dps460 0x5e > /sys/bus/i2c/devices/i2c-10/$1"
i2c_config "echo dps460 0x5e > /sys/bus/i2c/devices/i2c-11/$1"
#Attach PSU EEPROM
i2c_config "echo 24c02 0x56 > /sys/bus/i2c/devices/i2c-10/$1"
i2c_config "echo 24c02 0x56 > /sys/bus/i2c/devices/i2c-11/$1"
#Attach Fan EEPROM
i2c_config "echo 24c02 0x50 > /sys/bus/i2c/devices/i2c-15/$1"
i2c_config "echo 24c02 0x50 > /sys/bus/i2c/devices/i2c-16/$1"
i2c_config "echo 24c02 0x50 > /sys/bus/i2c/devices/i2c-17/$1"
;;
"delete_device") i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-2/$1"
i2c_config "echo 0x2c > /sys/bus/i2c/devices/i2c-7/$1"
i2c_config "echo 0x49 > /sys/bus/i2c/devices/i2c-7/$1"
i2c_config "echo 0x4a > /sys/bus/i2c/devices/i2c-7/$1"
i2c_config "echo 0x4b > /sys/bus/i2c/devices/i2c-7/$1"
i2c_config "echo 0x4c > /sys/bus/i2c/devices/i2c-7/$1"
i2c_config "echo 0x4f > /sys/bus/i2c/devices/i2c-7/$1"
i2c_config "echo 0x5e > /sys/bus/i2c/devices/i2c-10/$1"
i2c_config "echo 0x5e > /sys/bus/i2c/devices/i2c-11/$1"
i2c_config "echo 0x56 > /sys/bus/i2c/devices/i2c-10/$1"
i2c_config "echo 0x56 > /sys/bus/i2c/devices/i2c-11/$1"
i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-15/$1"
i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-16/$1"
i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-17/$1"
;;
*) echo "n3248pxe_platform: main_board_mux : invalid command !"
;;
esac
}

#Attach/Detach the SFP modules on PCA9548_2
switch_board_sfp() {
case $1 in
"new_device") i2c_config "echo optoe2 0x50 > /sys/bus/i2c/devices/i2c-20/$1"
i2c_config "echo optoe2 0x50 > /sys/bus/i2c/devices/i2c-21/$1"
i2c_config "echo optoe2 0x50 > /sys/bus/i2c/devices/i2c-22/$1"
i2c_config "echo optoe2 0x50 > /sys/bus/i2c/devices/i2c-23/$1"
;;
"delete_device") i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-20/$1"
i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-21/$1"
i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-22/$1"
i2c_config "echo 0x50 > /sys/bus/i2c/devices/i2c-23/$1"
;;
*) echo "n3248pxe_platform: switch_board_sfp: invalid command !"
;;
esac
}

#Forcibly bring quad-port phy out of reset for 48-1G port functionality

platform_firmware_versions() {

FIRMWARE_VERSION_FILE=/var/log/firmware_versions
rm -rf ${FIRMWARE_VERSION_FILE}
# Get BIOS version
echo "BIOS: `dmidecode -s system-version `" > $FIRMWARE_VERSION_FILE
# Get CPU CPLD version
echo "CPU CPLD: $((`cat /sys/devices/platform/dell-n3248pxe-cpld.0/cpu_cpld_mjr_ver`)).$((`cat /sys/devices/platform/dell-n3248pxe-cpld.0/cpu_cpld_mnr_ver`))" >> $FIRMWARE_VERSION_FILE
# Get SYS CPLD version
echo "SYS CPLD: $((`cat /sys/devices/platform/dell-n3248pxe-cpld.0/sys_cpld_mjr_ver`)).$((`cat /sys/devices/platform/dell-n3248pxe-cpld.0/sys_cpld_mnr_ver`))" >> $FIRMWARE_VERSION_FILE

}

install_python_api_package() {
device="/usr/share/sonic/device"
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)

rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl)
}

remove_python_api_package() {
rv=$(pip show sonic-platform > /dev/null 2>/dev/null)

rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -eq 0 ]; then
rv=$(pip3 uninstall -y sonic-platform > /dev/null 2>/dev/null)
fi
}

get_reboot_cause() {
REBOOT_REASON_FILE="/host/reboot-cause/platform/reboot_reason"
mkdir -p $(dirname $REBOOT_REASON_FILE)

# Handle First Boot into software version with reboot cause determination support
if [[ ! -e $REBOOT_REASON_FILE ]]; then
echo "0x0" > $REBOOT_REASON_FILE
else
cat /sys/devices/platform/dell-n3248pxe-cpld.0/reboot_cause > $REBOOT_REASON_FILE
fi
}


if [[ "$1" == "init" ]]; then
modprobe i2c-dev
modprobe i2c-mux-pca954x force_deselect_on_exit=1
modprobe pmbus
modprobe emc2305
modprobe dps200
modprobe dell_n3248pxe_platform

sys_devices "new_device"
get_reboot_cause
switch_board_sfp "new_device"
echo 0xf0 > /sys/devices/platform/dell-n3248pxe-cpld.0/sfp_txdis
install_python_api_package
platform_firmware_versions
elif [[ "$1" == "deinit" ]]; then
switch_board_sfp "delete_device"
sysdevices "delete_device"

modprobe -r dell_n3248pxe_platform

modprobe -r dps200
modprobe -r emc2305
modprobe -r pmbus
modprobe -r i2c-mux-pca954x
modprobe -r i2c-dev
remove_python_api_package
else
echo "n3248pxe_platform : Invalid option !"
fi
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/python
# This provies support for the following objects:
# * Onboard temperature sensors
# * FAN trays
# * PSU

import subprocess

output = ""
try:
rc = 0
output = subprocess.check_output('/usr/bin/sensors').splitlines()

valid = False
for line in output:
if line.startswith(b'acpitz') or line.startswith(b'coretemp'):
valid = True
if valid:
print (line)
if line == '': valid = False

print ("Onboard Temperature Sensors:")
idx = 0
for line in output:
if line.startswith(b'tmp75'):
print ('\t' + output[idx+2].split('(')[0])
idx += 1

print ("\nFanTrays:")
idx = 0
found_emc = False
for line in output:
if line.startswith(b'emc'):
found_emc = True
with open('/sys/devices/platform/dell-n3248pxe-cpld.0/fan0_prs') as f:
line = f.readline()
present = int(line, 0)
if present :
print ('\t' + 'FanTray1:')
print ('\t\t' + 'Fan Speed:' + (output[idx+2].split('(')[0]).split(':')[1])
with open('/sys/devices/platform/dell-n3248pxe-cpld.0/fan0_dir') as f:
line = f.readline()
dir = 'Intake' if line[:-1] == 'B2F' else 'Exhaust'
print ('\t\t' + 'Airflow:\t' + dir)
else : print ('\t' + 'FanTray1:\tNot Present')

with open('/sys/devices/platform/dell-n3248pxe-cpld.0/fan1_prs') as f:
line = f.readline()
present = int(line, 0)
if present :
print ('\t' + 'FanTray2:')
print ('\t\t' + 'Fan Speed:' + (output[idx+3].split('(')[0]).split(':')[1])
with open('/sys/devices/platform/dell-n3248pxe-cpld.0/fan1_dir') as f:
line = f.readline()
dir = 'Intake' if line[:-1] == 'B2F' else 'Exhaust'
print ('\t\t' + 'Airflow:\t' + dir)
else : print ('\t' + 'FanTray2:\tNot Present')

with open('/sys/devices/platform/dell-n3248pxe-cpld.0/fan2_prs') as f:
line = f.readline()
present = int(line, 0)
if present :
print ('\t' + 'FanTray3:')
print ('\t\t' + 'Fan Speed:' + (output[idx+4].split('(')[0]).split(':')[1])
with open('/sys/devices/platform/dell-n3248pxe-cpld.0/fan2_dir') as f:
line = f.readline()
dir = 'Intake' if line[:-1] == 'B2F' else 'Exhaust'
print ('\t\t' + 'Airflow:\t' + dir)
else : print ('\t' + 'FanTray3:\tNot Present')
idx += 1
if not found_emc :
print ('\t' + 'FanTray1:\tNot Present')
print ('\t' + 'FanTray2:\tNot Present')
print ('\t' + 'FanTray3:\tNot Present')

print ('\nPSUs:')
idx = 0
with open('/sys/devices/platform/dell-n3248pxe-cpld.0/psu0_prs') as f:
line = f.readline()
found_psu1 = int(line, 0)
if not found_psu1 :
print ('\tPSU1:\tNot Present')
with open('/sys/devices/platform/dell-n3248pxe-cpld.0/psu1_prs') as f:
line = f.readline()
found_psu2 = int(line, 0)
for line in output:
if line.startswith(b'dps460-i2c-10'):
with open('/sys/devices/platform/dell-n3248pxe-cpld.0/psu0_status') as f:
line = f.readline()
status = int(line, 0)
if not status :
print ('\tPSU1:\tNot OK')
break
with open('/sys/bus/i2c/devices/10-0056/eeprom') as f:
line = f.readline()
dir = 'Exhaust' if 'FORWARD' in line else 'Intake'
print ('\tPSU1:')
print ('\t\t' + output[idx+2].split('(')[0])
print ('\t\t' + output[idx+4].split('(')[0])
print ('\t\t' + output[idx+6].split('(')[0])
print ('\t\t' + output[idx+7].split('(')[0])
print ('\t\t' + output[idx+9].split('(')[0])
print ('\t\t' + output[idx+11].split('(')[0])
print ('\t\t' + output[idx+12].split('(')[0])
print ('\t\t' + output[idx+14].split('(')[0])
print ('\t\t' + output[idx+15].split('(')[0])
print ('\t\t' + 'Airflow:\t\t ' + dir)
if line.startswith(b'dps460-i2c-11'):
with open('/sys/devices/platform/dell-n3248pxe-cpld.0/psu1_status') as f:
line = f.readline()
status = int(line, 0)
if not status :
print ('\tPSU2:\tNot OK')
break
print ('\tPSU2:')
with open('/sys/bus/i2c/devices/11-0056/eeprom') as f:
line = f.readline()
dir = 'Exhaust' if 'FORWARD' in line else 'Intake'
print ('\t\t' + output[idx+2].split('(')[0])
print ('\t\t' + output[idx+4].split('(')[0])
print ('\t\t' + output[idx+6].split('(')[0])
print ('\t\t' + output[idx+7].split('(')[0])
print ('\t\t' + output[idx+9].split('(')[0])
print ('\t\t' + output[idx+11].split('(')[0])
print ('\t\t' + output[idx+12].split('(')[0])
print ('\t\t' + output[idx+14].split('(')[0])
print ('\t\t' + output[idx+15].split('(')[0])
print ('\t\t' + 'Airflow:\t\t ' + dir)
idx += 1
if not found_psu2 :
print ('\tPSU2:\tNot Present')

except subprocess.CalledProcessError as err:
print ("Exception when calling get_sonic_error -> %s\n" %(err))
rc = err.returncode
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/python
# Copyright (c) 2015 Dell Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
# LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS
# FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
#
# See the Apache Version 2.0 License for specific language governing
# permissions and limitations under the License.
#Script to read/write the portio based registers

import sys
import os
import getopt
import struct

resource='/dev/port'

def usage():
''' This is the Usage Method '''

print ('\t\t portiocfg.py --default')
print ('\t\t portiocfg.py --get --offset <offset>')
print ('\t\t portiocfg.py --set --val <val> --offset <offset>')
sys.exit(1)

def portio_reg_read(resource,offset):
fd=os.open(resource, os.O_RDONLY)
if(fd<0):
print ('file open failed %s"%resource')
return
if(os.lseek(fd, offset, os.SEEK_SET) != offset):
print ('lseek failed on %s'%resource)
return
buf=os.read(fd,1)
reg_val1=ord(buf)
print ('reg value %x'%reg_val1)
os.close(fd)

def portio_reg_write(resource,offset,val):
fd=os.open(resource,os.O_RDWR)
if(fd<0):
print ('file open failed %s"%resource')
return
if(os.lseek(fd, offset, os.SEEK_SET) != offset):
print ('lseek failed on %s'%resource)
return
ret=os.write(fd,struct.pack('B',val))
if(ret != 1):
print ('write failed %d'%ret)
return
os.close(fd)

def main(argv):

''' The main function will read the user input from the
command line argument and process the request '''

opts = ''
val = ''
choice = ''
offset = ''

try:
opts, args = getopt.getopt(argv, "hgs:" , \
["val=","offset=","help", "get", "set"])

except getopt.GetoptError:
usage()

for opt,arg in opts:

if opt in ('-h','--help'):
choice = 'help'

elif opt in ('-g', '--get'):
choice = 'get'

elif opt in ('-s', '--set'):
choice = 'set'

elif opt == '--offset':
offset = int(arg,16)

elif opt == '--val':
val = int(arg,16)

if choice == 'get' and offset != '':
portio_reg_read(resource,offset)

elif choice == 'set' and offset != '' and val != '':
portio_reg_write(resource,offset,val)

else:
usage()

#Calling the main method
if __name__ == "__main__":
main(sys.argv[1:])

Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env python2

"""
port_notify
port notify status change for SONiC
"""

try:
from datetime import datetime
from swsscommon import swsscommon
from sonic_py_common import daemon_base, logger
except ImportError as e:
raise ImportError (str(e) + " - required module not found")

#
# Constants ====================================================================
#

SYSLOG_IDENTIFIER = "port_notify"


STATE_PORT_TABLE = 'PORT_TABLE'


RJ45_PORT_START = 0;
RJ45_PORT_END = 47;

# Global logger class instance
helper_logger = logger.Logger(SYSLOG_IDENTIFIER)

XCVR_STATE_EMPTY = 0
XCVR_STATE_ERROR = 1
XCVR_STATE_INCOMP = 2
XCVR_STATE_CONFIG = 3
XCVR_STATE_READY = 4
XCVR_STATE_TIMEOUT = 5

xcvr_state_tbl = {
XCVR_STATE_EMPTY: { "xcvr_state": "N/A", "xcvr_app_status": "down" },
XCVR_STATE_ERROR: { "xcvr_state": "Error", "xcvr_app_status": "down" },
XCVR_STATE_INCOMP: { "xcvr_state": "Incompatible", "xcvr_app_status": "up" },
XCVR_STATE_CONFIG: { "xcvr_state": "Config", "xcvr_app_status": "down" },
XCVR_STATE_TIMEOUT: { "xcvr_state": "Timeout", "xcvr_app_status": "up" },
XCVR_STATE_READY: { "xcvr_state": "Ready", "xcvr_app_status": "up" }
}

# Wait for port init is done
def wait_for_port_init_done():
# Connect to APPL_DB and subscribe to PORT table notifications
appl_db = daemon_base.db_connect("APPL_DB")

sel = swsscommon.Select()
sst = swsscommon.SubscriberStateTable(appl_db, swsscommon.APP_PORT_TABLE_NAME)
sel.addSelectable(sst)

# Make sure this daemon started after all port configured
while True:
(state, c) = sel.select(1000)
if state == swsscommon.Select.TIMEOUT:
continue
if state != swsscommon.Select.OBJECT:
helper_logger.log_warning("sel.select() did not return swsscommon.Select.OBJECT")
continue

(key, op, fvp) = sst.pop()

# Wait until PortInitDone
if key in ["PortInitDone"]:
break

def notify_port_xcvr_status(port_name, app_status_port_tbl, state_port_tbl, flag):

fvs = swsscommon.FieldValuePairs([("xcvr_status", xcvr_state_tbl[flag]["xcvr_app_status"])])
tm = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
state_fvs = swsscommon.FieldValuePairs([("xcvr_status", xcvr_state_tbl[flag]["xcvr_state"]), ("xcvr_time", tm)])

state_port_tbl.set(port_name, state_fvs)

app_status_port_tbl.set(port_name, fvs)

helper_logger.log_notice("Port {} xcvr_app_status change to {}".format(port_name, xcvr_state_tbl[flag]["xcvr_app_status"]))
return True


def main():
helper_logger.log_notice("Start port_notify")
# Connect to APP_DB and create transceiver dom info table
appl_db = daemon_base.db_connect("APPL_DB")

app_status_port_tbl = swsscommon.ProducerStateTable(appl_db,
swsscommon.APP_PORT_APP_STATUS_TABLE_NAME)

state_db = daemon_base.db_connect("STATE_DB")
state_port_tbl = swsscommon.Table(state_db, STATE_PORT_TABLE)

# Wait for PortInitDone
wait_for_port_init_done()

for port in range(RJ45_PORT_START, RJ45_PORT_END+1):
#print "Ethernet{}".format(port)
notify_port_xcvr_status("Ethernet{}".format(port), app_status_port_tbl, state_port_tbl, XCVR_STATE_READY)

helper_logger.log_notice("End port_notify")


if __name__ == '__main__':
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
#docker exec -i pmon sensors "$@"
docker exec -i pmon /usr/bin/platform_sensors.py "$@"

#To probe sensors not part of lm-sensors
#if [ -r /usr/local/bin/platform_sensors.py ]; then
# python /usr/local/bin/platform_sensors.py
#fi
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""
Module sonic_platform provides the platform dependent population of
platform.py, chassis.py, component.py, sfp.py, thermal.py, psu.py,
fan.py and watchdog.py
"""
__all__ = ["platform", "chassis", "sfp", "eeprom", "component", "thermal", "psu", "fan", "fan_drawer", "watchdog"]
from sonic_platform import *

Original file line number Diff line number Diff line change
@@ -0,0 +1,344 @@
#!/usr/bin/env python

#############################################################################
# DELLEMC N3248PXE
#
# Module contains an implementation of SONiC Platform Base API and
# provides the platform information
#
#############################################################################

try:
import os
import sys
import time
from sonic_platform_base.chassis_base import ChassisBase
from sonic_platform.sfp import Sfp
from sonic_platform.eeprom import Eeprom
from sonic_platform.component import Component
from sonic_platform.psu import Psu
from sonic_platform.thermal import Thermal
from sonic_platform.watchdog import Watchdog
from sonic_platform.fan import Fan
from sonic_platform.fan_drawer import FanDrawer
except ImportError as e:
raise ImportError(str(e) + "- required module not found")


MAX_N3248PXE_FANTRAY = 3
MAX_N3248PXE_FAN = 1
MAX_N3248PXE_PSU = 2
MAX_N3248PXE_THERMAL = 5
MAX_N3248PXE_COMPONENT = 3 # BIOS, CPU CPLD and SYS CPLD

media_part_num_list = set([ \
"8T47V","XTY28","MHVPK","GF76J","J6FGD","F1KMV","9DN5J","H4DHD","6MCNV","0WRX0","X7F70","5R2PT","WTRD1","WTRD1","WTRD1","WTRD1","5250G","WTRD1","C5RNH","C5RNH","FTLX8571D3BCL-FC",
"C5RNH","5250G","N8TDR","7D64H","7D64H","RN84N","RN84N","HMTNW","6K3Y6","6K3Y6","TY5FM","50M0R","PGYJT","WP2PP","85Y13","1HCGH","FP9R1","FYD0M","C6Y7M","C6Y7M","V250M","V250M",
"5CWK6","5CWK6","53HVN","53HVN","358VV","358VV","MV799","MV799","YJF03","P9GND","T1KCN","1DXKP","MT7R2","K0T7R","W5G04","7TCDN","7TCDN","7TCDN","7TCDN","7TCDN","V3XJK","0MV31",
"5FVP7","N6KM9","C41MF","77KC3","XW7J0","V4NJV","2XJHY","H93DH","H93DH","F8CG0","F8CG0","F8CG0","119N6","WFMF5","794RX","288F6","1M31V","1M31V","5NP8R","5NP8R","4TC09","4TC09",
"FC6KV","FC6KV","J90VN","J90VN","05RH0","05RH0","YDN52","0C2YV","YDN52","0C2YV","9JT65","D7M6H","6GW14","FYVFW","0VF5H","P4YPY","P4YPY","TCPM2","TCPM2","JNPF8","JNPF8","27GG5",
"27GG5","P8T4W","P8T4W","JR54Y","M6N0J","XJYD0","K44H9","035KG","P7C7N","76V43","3CC35","FN4FC","26FN3","YFNDD","YFNDD","7R9N9","035KG","P7C7N","76V43","3CC35","PLRXPLSCS43811",
"FN4FC","26FN3","YFNDD","YFNDD","7R9N9","G86YJ","V407F","V407F","9KH6T","G86YJ","V407F","9KH6T","2JVDD","D0R73","VXFJY","9X8JP","2JVDD","D0R73","VXFJY","9X8JP","2JVDD","D0R73","VXFJY",
"9X8JP","GMFC5","GMFC5","GMFC5","D7P80","3MFXG","3MFXG","0GWXJ","THPF3","THPF3","THPF3","THPF3","THPF3","PJ62G","3XCX1","JJYKG","RRRTK","16K56","86JM2","K5R6C","7MG2C","WTPPN","9HTT2",
"NKM4F","VXGGG","JC9W6","6MR8M","RP3GV","M5PPJ","XKY55","TKCXT","05J8P","5WGKD","XFDRT","NW8DM","YPKH3","5WGKD","XFDRT","NW8DM","YPKH3","71XXK","MVCX6","0XYP6","HPPVW","3GHRT","71XXK",
"MVCX6","0XYP6","HPPVW","3GHRT","2X5T6","135V2","KD5MV","2X5T6","KD5MV","HHFK0","3YWG7","5CMT2","RCVP5","X5DH4","HHFK0","3YWG7","5CMT2","RCVP5","X5DH4","3YWG7","5CMT2","RCVP5","X5DH4",
"4WJ41","4WJ41","14NV5","14NV5","14NV5","4WGYD","YKMH7","X7CCC","X7CCC","0X9CT","0CY8V","P7D7R","W4GPP","W4GPP","W4GPP","HHHCHC","07RN7","07RN7","0YR96","0YR96","JCYM9","FTLX8571D3BCL",
"DDW0X","VPFDJ","229KM","9FC7D","DDW0X","VPFDJ","6FMR5","J7K20","N3K9W","6FMR5","8R4VM","7VN5T","D9YM8","8R4VM","VYXPW","87TPX","WY6FK","VYXPW","87TPX","WY6FK","WG8C4","N8K82","2DV6Y",
"77C3C","RC0HM","77C3C","RC0HM","JHXTN","3P3PG","92YVM","4VX5M","4VX5M","6RRGD","W4JWV","22V6R","XR11M","9GMDY","JMCWK","TP2F0","6MGDY","78RHK", "C0TP5","0WDNV","FCLF8522P2BTL"\
])
class Chassis(ChassisBase):
"""
DELLEMC Platform-specific Chassis class
"""
CPLD_DIR = '/sys/devices/platform/dell-n3248pxe-cpld.0/'

_global_port_pres_dict = {}

_sfpp_port_to_i2c_mapping = {
49: 20,
50: 21,
51: 22,
52: 23,
53: 24,
54: 25,
}

def __init__(self):
ChassisBase.__init__(self)
# sfp.py will read eeprom contents and retrive the eeprom data.
# We pass the eeprom path from chassis.py
self.PORT_START = 1
self.PORT_END = 54
self.PORTS_IN_BLOCK = (self.PORT_END + 1)
self.SFP_PORT_START = 49
self._sfp_port = range(self.SFP_PORT_START, self.PORTS_IN_BLOCK)
eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom"
for index in range(self.PORT_START, self.PORTS_IN_BLOCK):
eeprom_path = ''
if index in self._sfp_port:
eeprom_path = eeprom_base.format(self._sfpp_port_to_i2c_mapping[index])
sfp_node = Sfp(index, 'SFP', eeprom_path)
self._sfp_list.append(sfp_node)

self._eeprom = Eeprom()
self._watchdog = Watchdog()
self._num_sfps = 54
self._num_fans = MAX_N3248PXE_FANTRAY * MAX_N3248PXE_FAN
self._fan_list = [Fan(i, j) for i in range(MAX_N3248PXE_FANTRAY) \
for j in range(MAX_N3248PXE_FAN)]
for k in range(MAX_N3248PXE_FANTRAY):
fandrawer = FanDrawer(k)
self._fan_drawer_list.append(fandrawer)
self._fan_list.extend(fandrawer._fan_list)

self._psu_list = [Psu(i) for i in range(MAX_N3248PXE_PSU)]
self._thermal_list = [Thermal(i) for i in range(MAX_N3248PXE_THERMAL)]
self._component_list = [Component(i) for i in range(MAX_N3248PXE_COMPONENT)]
for port_num in self._sfp_port:
# sfp get uses zero-indexing, but port numbers start from 1
presence = self.get_sfp(port_num).get_presence()
self._global_port_pres_dict[port_num] = '1' if presence else '0'

self._watchdog = Watchdog()
self.locator_led_reg = "locator_led"
self.LOCATOR_LED_ON = "blink_blue"
self.LOCATOR_LED_OFF = self.STATUS_LED_COLOR_OFF

def _get_cpld_register(self, reg_name):
# On successful read, returns the value read from given
# reg name and on failure rethrns 'ERR'
cpld_reg_file = self.CPLD_DIR + '/' + reg_name
try:
rv = open(cpld_reg_file, 'r').read()
except IOError : return 'ERR'
return rv.strip('\r\n').lstrip(' ')

def _set_cpld_register(self, reg_name, value):
# On successful write, returns the value will be written on
# reg_name and on failure returns 'ERR'
rv = 'ERR'
cpld_reg_file = self.CPLD_DIR + '/' + reg_name

if (not os.path.isfile(cpld_reg_file)):
return rv

try:
with open(cpld_reg_file, 'w') as fd:
rv = fd.write(str(value))
except Exception:
rv = 'ERR'

return rv

# check for this event change for sfp / do we need to handle timeout/sleep

def get_change_event(self, timeout=0):
"""
Returns a nested dictionary containing all devices which have
experienced a change at chassis level
"""
port_dict = {}
change_dict = {}
change_dict['sfp'] = port_dict
while True:
for port_num in self._sfp_port:
# sfp get uses zero-indexing, but port numbers start from 1
presence = self.get_sfp(port_num).get_presence()
if(presence and self._global_port_pres_dict[port_num] == '0'):
self._global_port_pres_dict[port_num] = '1'
port_dict[port_num] = '1'
elif(not presence and self._global_port_pres_dict[port_num] == '1'):
self._global_port_pres_dict[port_num] = '0'
port_dict[port_num] = '0'

if(len(port_dict) > 0):
return True, change_dict

time.sleep(0.5)



def get_sfp(self, index):
"""
Retrieves sfp represented by (0-based) index <index>
Args:
index: An integer, the index (0-based) of the sfp to retrieve.
The index should be the sequence of a physical port in a chassis,
starting from 0.
For example, 0 for Ethernet0, 1 for Ethernet4 and so on.
Returns:
An object dervied from SfpBase representing the specified sfp
"""
sfp = None

try:
# The index will start from 0
sfp = self._sfp_list[index-1]
except IndexError:
sys.stderr.write("SFP index {} out of range (0-{})\n".format(
index, len(self._sfp_list)-1))
return sfp

def get_name(self):
"""
Retrieves the name of the chassis
Returns:
string: The name of the chassis
"""
return self._eeprom.modelstr().decode()

def get_presence(self):
"""
Retrieves the presence of the chassis
Returns:
bool: True if chassis is present, False if not
"""
return True

def get_model(self):
"""
Retrieves the model number (or part number) of the chassis
Returns:
string: Model/part number of chassis
"""
return self._eeprom.part_number_str()

def get_serial(self):
"""
Retrieves the serial number of the chassis (Service tag)
Returns:
string: Serial number of chassis
"""
return self._eeprom.serial_str()

def get_status(self):
"""
Retrieves the operational status of the chassis
Returns:
bool: A boolean value, True if chassis is operating properly
False if not
"""
return True

def get_base_mac(self):
"""
Retrieves the base MAC address for the chassis
Returns:
A string containing the MAC address in the format
'XX:XX:XX:XX:XX:XX'
"""
return self._eeprom.base_mac_addr('')

def get_serial_number(self):
"""
Retrieves the hardware serial number for the chassis
Returns:
A string containing the hardware serial number for this chassis.
"""
return self._eeprom.serial_number_str()

def get_system_eeprom_info(self):
"""
Retrieves the full content of system EEPROM information for the chassis
Returns:
A dictionary where keys are the type code defined in
OCP ONIE TlvInfo EEPROM format and values are their corresponding
values.
"""
return self._eeprom.system_eeprom_info()

def get_reboot_cause(self):
"""
Retrieves the cause of the previous reboot
Returns:
A tuple (string, string) where the first element is a string
containing the cause of the previous reboot. This string must be
one of the predefined strings in this class. If the first string
is "REBOOT_CAUSE_HARDWARE_OTHER", the second string can be used
to pass a description of the reboot cause.
"""

reset_reason = int(self._get_cpld_register('reboot_cause'), 16)

if (reset_reason & 0x02) :
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, 'Shutdown by CPU')
elif (reset_reason & 0x04) :
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, "Failed to boot from configured boot device")
elif (reset_reason & 0x8) :
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, "Booted from Backup BIOS")
elif (reset_reason & 0x10) :
return(ChassisBase.REBOOT_CAUSE_WATCHDOG, None)
elif (reset_reason & 0x20):
return(ChassisBase.REBOOT_CAUSE_THERMAL_OVERLOAD_CPU)
elif (reset_reason & 0x40) :
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, 'Warm Reset')
elif (reset_reason & 0x80) :
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, 'Cold Reset')
elif (reset_reason & 0x01) :
return (ChassisBase.REBOOT_CAUSE_POWER_LOSS, None)

def get_eeprom(self):
"""
Retrieves the Sys Eeprom instance for the chassis.
Returns :
The instance of the Sys Eeprom
"""
return self._eeprom

def get_num_fans(self):
"""
Retrives the number of Fans on the chassis.
Returns :
An integer represents the number of Fans on the chassis.
"""
return self._num_fans

def get_num_sfps(self):
"""
Retrives the numnber of Media on the chassis.
Returns:
An integer represences the number of SFPs on the chassis.
"""
return self._num_sfps

def get_qualified_media_list(self):
return media_part_num_list

def set_locator_led(self, color):
"""
Sets the state of the Chassis Locator LED
Args:
color: A string representing the color with which to set the Chassis Locator LED
Returns:
bool: True if the Chassis Locator LED state is set successfully, False if not
"""
if color == self.LOCATOR_LED_ON or color == self.LOCATOR_LED_OFF:
rv = self._set_cpld_register(self.locator_led_reg, color)
if (rv != 'ERR'):
return True
else:
return False

def get_locator_led(self):
"""
Gets the state of the Chassis Locator LED
Returns:
LOCATOR_LED_ON or LOCATOR_LED_OFF
"""
loc_led = self._get_cpld_register(self.locator_led_reg)
if (loc_led != 'ERR'):
# Actually driver returns the color code 'blink_blue'
# Returning "blue_blink" to make it common to all platforms output
if (loc_led == self.LOCATOR_LED_ON):
self.LOCATOR_LED_ON = self.STATUS_LED_COLOR_BLUE_BLINK
return self.LOCATOR_LED_ON
else:
return self.LOCATOR_LED_OFF
else:
return self.LOCATOR_LED_OFF
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env python

########################################################################
# DELLEMC N3248PXE
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Components' (e.g., BIOS, CPLD, FPGA, BMC etc.) available in
# the platform
#
########################################################################

try:
import subprocess
from sonic_platform_base.component_base import ComponentBase

except ImportError as e:
raise ImportError(str(e) + "- required module not found")
def get_bios_version():
return subprocess.check_output(['dmidecode', '-s', 'system-version']).strip().decode()

def get_cpld_version(cpld):
mjr_ver=subprocess.check_output('cat /sys/devices/platform/dell-n3248pxe-cpld.0/' + cpld + '_mjr_ver', shell=True).strip()[2:].decode()
mnr_ver=subprocess.check_output('cat /sys/devices/platform/dell-n3248pxe-cpld.0/' + cpld + '_mnr_ver', shell=True).strip()[2:].decode()
return (str(mjr_ver) + '.' + str(mnr_ver))

class Component(ComponentBase):
"""DellEMC Platform-specific Component class"""

CHASSIS_COMPONENTS = [
['BIOS',
'Performs initialization of hardware components during booting',
get_bios_version()
],
['CPU CPLD',
'Used for managing the CPU power sequence and CPU states',
get_cpld_version('cpu_cpld')
],
['SYS CPLD',
'Used for managing FAN, PSU, SFP modules (1-48) SFP Plus modules (49-62)',
get_cpld_version('sys_cpld')
]
]

def __init__(self, component_index=0):
self.index = component_index
self.name = self.CHASSIS_COMPONENTS[self.index][0]
self.description = self.CHASSIS_COMPONENTS[self.index][1]
self.version = self.CHASSIS_COMPONENTS[self.index][2]

def get_name(self):
"""
Retrieves the name of the component
Returns:
A string containing the name of the component
"""
return self.name

def get_description(self):
"""
Retrieves the description of the component
Returns:
A string containing the description of the component
"""
return self.description

def get_firmware_version(self):
"""
Retrieves the firmware version of the component
Returns:
A string containing the firmware version of the component
"""
return self.version

def install_firmware(self, image_path):
"""
Installs firmware to the component
Args:
image_path: A string, path to firmware image
Returns:
A boolean, True if install was successful, False if not
"""
return False
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/usr/bin/env python

#############################################################################
# DellEmc N3248PXE
#
# Platform and model specific eeprom subclass, inherits from the base class,
# and provides the followings:
# - the eeprom format definition
# - specific encoder/decoder if there is special need
#############################################################################
try:
import os.path
from sonic_eeprom import eeprom_tlvinfo
import binascii
except ImportError as e:
raise ImportError(str(e) + "- required module not found")


class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):

def __init__(self):
self.eeprom_path = None
f = '/sys/class/i2c-adapter/i2c-2/2-0050/eeprom'
if not os.path.exists(f):
return
self.eeprom_path = f
super(Eeprom, self).__init__(self.eeprom_path, 0, '', True)
self.eeprom_tlv_dict = dict()
try:
self.eeprom_data = self.read_eeprom()
except Exception:
self.eeprom_data = "N/A"
raise RuntimeError("Eeprom is not Programmed")

eeprom = self.eeprom_data

if not self.is_valid_tlvinfo_header(eeprom):
return

total_length = ((eeprom[9]) << 8) | (eeprom[10])
tlv_index = self._TLV_INFO_HDR_LEN
tlv_end = self._TLV_INFO_HDR_LEN + total_length

while (tlv_index + 2) < len(eeprom) and tlv_index < tlv_end:
if not self.is_valid_tlv(eeprom[tlv_index:]):
break

tlv = eeprom[tlv_index:tlv_index + 2
+ (eeprom[tlv_index + 1])]
code = "0x%02X" % ((tlv[0]))


name, value = self.decoder(None, tlv)

self.eeprom_tlv_dict[code] = value
if (eeprom[tlv_index]) == self._TLV_CODE_CRC_32:
break

tlv_index += (eeprom[tlv_index+1]) + 2

def serial_number_str(self):
"""
Returns the serial number
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_SERIAL_NUMBER)
if not is_valid:
return "N/A"
return results[2]

def base_mac_addr(self, e):
"""
Returns the base mac address found in the system EEPROM
"""
(is_valid, t) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_MAC_BASE)
if not is_valid or t[1] != 6:
return super(eeprom_tlvinfo.TlvInfoDecoder, self).switchaddrstr(t)

return ":".join([binascii.b2a_hex(T) for T in t[2]])

def modelstr(self):
"""
Returns the Model name
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_PRODUCT_NAME)
if not is_valid:
return "N/A"

return results[2]

def part_number_str(self):
"""
Returns the part number
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_PART_NUMBER)
if not is_valid:
return "N/A"

return results[2]

def serial_str(self):
"""
Returns the servicetag number
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_SERVICE_TAG)
if not is_valid:
return "N/A"

return results[2]

def revision_str(self):
"""
Returns the device revision
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_DEVICE_VERSION)
if not is_valid:
return "N/A"

return results[2]

def system_eeprom_info(self):
"""
Returns a dictionary, where keys are the type code defined in
ONIE EEPROM format and values are their corresponding values
found in the system EEPROM.
"""
return self.eeprom_tlv_dict


Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#!/usr/bin/env python

########################################################################
# DellEMC N3248PXE
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Fans' information which are available in the platform.
#
########################################################################
try:
import os
from sonic_platform_base.fan_base import FanBase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")


class Fan(FanBase):
"""DellEMC Platform-specific Fan class"""

def __init__(self, fantray_index=0, fan_index=0, psu_fan=False, dependency=None):
self.is_psu_fan = psu_fan

if not self.is_psu_fan:
# API index is starting from 0, DellEMC platform index is
# starting from 1
self.presence_reg = "fan{}_prs".format(fantray_index)
self.dir_reg = "fan{}_dir".format(fantray_index)
self.rpm_file = "/sys/bus/i2c/devices/7-002c/fan{}_input".format(fantray_index+1)
self.eeprom = "/sys/bus/i2c/devices/{}-0050/eeprom".format(15 + fantray_index)
self.fantray_index = fantray_index
else:
self.psu_index = fantray_index
self.dependancy = dependency
self.dir_reg = ""
self.dps_hwmon = "/sys/bus/i2c/devices/{}-005e/hwmon/".format(fantray_index+10)
self.eeprom = "/sys/bus/i2c/devices/{}-0056/eeprom".format(10 + fantray_index)
self.max_speed = 0

def _get_cpld_register(self, reg_name):
# On successful read, returns the value read from given
# reg name and on failure rethrns 'ERR'
cpld_dir = "/sys/devices/platform/dell-n3248pxe-cpld.0/"
cpld_reg_file = cpld_dir + '/' + reg_name
try:
buf = open(cpld_reg_file, 'r').read()
except (IOError, AttributeError):
return 'ERR'
return buf.strip('\r\n').lstrip(' ')

def get_name(self):
"""
Retrieves the name of the device
Returns:
String: The name of the device
"""
if self.is_psu_fan:
return "PSU{} Fan".format(self.psu_index)
else:
return "Fan{}".format(self.fantray_index+1)

def get_model(self):
"""
Retrieves the part number of the FAN
Returns:
String: Part number of FAN
"""
try:
val = open(self.eeprom, "rb").read()[13:19]
except Exception:
val = None
return val.decode()

def get_serial(self):
"""
Retrieves the serial number of the FAN
Returns:
String: Serial number of FAN
"""
try:
val = open(self.eeprom, "rb").read()[21:41]
except Exception:
val = None
return val.decode()

def get_presence(self):
"""
Retrieves the presence of the FAN
Returns:
bool: True if fan is present, False if not
"""

if self.is_psu_fan:
return False #--- TBD --- #

presence = self._get_cpld_register(self.presence_reg)
if presence == 'ERR':
return False
if int(presence,0) == 1:
return True

def get_status(self):
"""
Retrieves the operational status of the FAN
Returns:
bool: True if FAN is operating properly, False if not
"""
return True

def get_direction(self):
"""
Retrieves the fan airfow direction
Returns:
A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST
depending on fan direction
Notes:
In DellEMC platforms,
- Forward/Exhaust : Air flows from Port side to Fan side.
- Reverse/Intake : Air flows from Fan side to Port side.
"""
if not self.is_psu_fan:
val = self._get_cpld_register(self.dir_reg)
direction = 'Exhaust' if val == 'F2B' else 'Intake'
if direction == 'ERR':
return None
else:
try:
val = open(self.eeprom, "rb").read()[0xe1:0xe8]
except Exception:
return None
direction = 'Exhaust' if val == 'FORWARD' else 'Intake'
return direction

def get_speed(self):
"""
Retrieves the speed of the fan
Returns:
int: percentage of the max fan speed
"""
if self.max_speed == 0:
self.max_speed = 23500
fan_speed = 0
try:
if not self.is_psu_fan:
rpm_file = self.rpm_file
else:
dps_dir = self.dps_hwmon + '/' + os.listdir(self.dps_hwmon)[0]
rpm_file = dps_dir + '/' + 'fan1_input'
fan_speed = int(open(rpm_file, "rb").read())
except Exception:
return None
speed = (100 * fan_speed)//self.max_speed
return speed

def get_speed_rpm(self):
"""
Retrieves the speed of the fan
Returns:
int: percentage of the max fan speed
"""
fan_speed = 0
try:
if not self.is_psu_fan:
rpm_file = self.rpm_file
else:
dps_dir = self.dps_hwmon + '/' + os.listdir(self.dps_hwmon)[0]
rpm_file = dps_dir + '/' + 'fan1_input'
fan_speed = int(open(rpm_file, "rb").read())
except Exception:
return None
return fan_speed
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env python

########################################################################
# DellEMC N3248PXE
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Fan-Drawers' information available in the platform.
#
########################################################################

try:
from sonic_platform_base.fan_drawer_base import FanDrawerBase
from sonic_platform.fan import Fan
except ImportError as e:
raise ImportError(str(e) + "- required module not found")

N3248PXE_FANS_PER_FANTRAY = 2


class FanDrawer(FanDrawerBase):
"""DellEMC Platform-specific Fan class"""

def __init__(self, fantray_index):

FanDrawerBase.__init__(self)
# FanTray is 1-based in DellEMC platforms
self.fantrayindex = fantray_index + 1
for i in range(N3248PXE_FANS_PER_FANTRAY):
self._fan_list.append(Fan(fantray_index, i))

def get_name(self):
"""
Retrieves the fan drawer name
Returns:
string: The name of the device
"""
return "FanTray{}".format(self.fantrayindex)
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
#!/usr/bin/python3

########################################################################
# DellEMC
#
# Module contains implementation of IpmiSensor and IpmiFru classes that
# provide Sensor's and FRU's information respectively.
#
########################################################################

import subprocess
import re

# IPMI Request Network Function Codes
NetFn_SensorEvent = 0x04
NetFn_Storage = 0x0A

# IPMI Sensor Device Commands
Cmd_GetSensorReadingFactors = 0x23
Cmd_GetSensorThreshold = 0x27
Cmd_GetSensorReading = 0x2D

# IPMI FRU Device Commands
Cmd_ReadFRUData = 0x11

def get_ipmitool_raw_output(args):
"""
Returns a list the elements of which are the individual bytes of
ipmitool raw <cmd> command output.
"""
result_bytes = list()
result = ""
command = "ipmitool raw {}".format(args)
try:
proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE,
universal_newlines=True, stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
if not proc.returncode:
result = stdout.rstrip('\n')
except EnvironmentError:
pass

for i in result.split():
result_bytes.append(int(i, 16))

return result_bytes

class IpmiSensor(object):

# Sensor Threshold types and their respective bit masks
THRESHOLD_BIT_MASK = {
"LowerNonCritical" : 0,
"LowerCritical" : 1,
"LowerNonRecoverable" : 2,
"UpperNonCritical" : 3,
"UpperCritical" : 4,
"UpperNonRecoverable" : 5
}

def __init__(self, sensor_id, is_discrete=False):
self.id = sensor_id
self.is_discrete = is_discrete

def _get_converted_sensor_reading(self, raw_value):
"""
Returns a 2 element tuple(bool, int) in which first element
provides the validity of the reading and the second element is
the converted sensor reading
"""
# Get Sensor Reading Factors
cmd_args = "{} {} {} {}".format(NetFn_SensorEvent,
Cmd_GetSensorReadingFactors,
self.id, raw_value)
factors = get_ipmitool_raw_output(cmd_args)

if len(factors) != 7:
return False, 0

# Compute Twos complement
def get_twos_complement(val, bits):
if val & (1 << (bits - 1)):
val = val - (1 << bits)
return val

# Calculate actual sensor value from the raw sensor value
# using the sensor reading factors.
M = get_twos_complement(((factors[2] & 0xC0) << 8) | factors[1], 10)
B = get_twos_complement(((factors[4] & 0xC0) << 8) | factors[3], 10)
R_exp = get_twos_complement((factors[6] & 0xF0) >> 4, 4)
B_exp = get_twos_complement(factors[6] & 0x0F, 4)

converted_reading = ((M * raw_value) + (B * 10**B_exp)) * 10**R_exp

return True, converted_reading

def get_reading(self):
"""
For Threshold sensors, returns the sensor reading.
For Discrete sensors, returns the state value.
Returns:
A tuple (bool, int) where the first element provides the
validity of the reading and the second element provides the
sensor reading/state value.
"""
# Get Sensor Reading
cmd_args = "{} {} {}".format(NetFn_SensorEvent, Cmd_GetSensorReading,
self.id)
output = get_ipmitool_raw_output(cmd_args)
if len(output) != 4:
return False, 0

# Check reading/state unavailable
if output[1] & 0x20:
return False, 0

if self.is_discrete:
state = ((output[3] & 0x7F) << 8) | output[2]
return True, state
else:
return self._get_converted_sensor_reading(output[0])

def get_threshold(self, threshold_type):
"""
Returns the sensor's threshold value for a given threshold type.
Args:
threshold_type (str) - one of the below mentioned
threshold type strings
"LowerNonCritical"
"LowerCritical"
"LowerNonRecoverable"
"UpperNonCritical"
"UpperCritical"
"UpperNonRecoverable"
Returns:
A tuple (bool, int) where the first element provides the
validity of that threshold and second element provides the
threshold value.
"""
# Thresholds are not valid for discrete sensors
if self.is_discrete:
raise TypeError("Threshold is not applicable for Discrete Sensor")

if threshold_type not in list(self.THRESHOLD_BIT_MASK.keys()):
raise ValueError("Invalid threshold type {} provided. Valid types "
"are {}".format(threshold_type,
list(self.THRESHOLD_BIT_MASK.keys())))

bit_mask = self.THRESHOLD_BIT_MASK[threshold_type]

# Get Sensor Threshold
cmd_args = "{} {} {}".format(NetFn_SensorEvent, Cmd_GetSensorThreshold,
self.id)
thresholds = get_ipmitool_raw_output(cmd_args)
if len(thresholds) != 7:
return False, 0

valid_thresholds = thresholds.pop(0)
# Check whether particular threshold is readable
if valid_thresholds & (1 << bit_mask):
return self._get_converted_sensor_reading(thresholds[bit_mask])
else:
return False, 0

class IpmiFru(object):

def __init__(self, fru_id):
self.id = fru_id

def _get_ipmitool_fru_print(self):
result = ""
command = "ipmitool fru print {}".format(self.id)
try:
proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE,
universal_newlines=True, stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
if not proc.returncode:
result = stdout.rstrip('\n')
except EnvironmentError:
pass

return result

def _get_from_fru(self, info):
"""
Returns a string containing the info from FRU
"""
fru_output = self._get_ipmitool_fru_print()
if not fru_output:
return "NA"

info_req = re.search(r"%s\s*:(.*)" % info, fru_output)
if not info_req:
return "NA"

return info_req.group(1).strip()

def get_board_serial(self):
"""
Returns a string containing the Serial Number of the device.
"""
return self._get_from_fru('Board Serial')

def get_board_part_number(self):
"""
Returns a string containing the Part Number of the device.
"""
return self._get_from_fru('Board Part Number')

def get_board_mfr_id(self):
"""
Returns a string containing the manufacturer id of the FRU.
"""
return self._get_from_fru('Board Mfg')

def get_board_product(self):
"""
Returns a string containing the manufacturer id of the FRU.
"""
return self._get_from_fru('Board Product')

def get_fru_data(self, offset, count=1):
"""
Reads and returns the FRU data at the provided offset.
Args:
offset (int) - FRU offset to read
count (int) - Number of bytes to read [optional, default = 1]
Returns:
A tuple (bool, list(int)) where the first element provides
the validity of the data read and the second element is a
list, the elements of which are the individual bytes of the
FRU data read.
"""
result_bytes = list()
is_valid = True
result = ""

offset_LSB = offset & 0xFF
offset_MSB = offset & 0xFF00
command = "ipmitool raw {} {} {} {} {} {}".format(NetFn_Storage,
Cmd_ReadFRUData,
self.id, offset_LSB,
offset_MSB, count)
try:
proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE,
universal_newlines=True, stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
if not proc.returncode:
result = stdout.rstrip('\n')
except EnvironmentError:
is_valid = False

if (not result) or (not is_valid):
return False, result_bytes

for i in result.split():
result_bytes.append(int(i, 16))

read_count = result_bytes.pop(0)
if read_count != count:
return False, result_bytes
else:
return True, result_bytes
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env python

#############################################################################
#
# Module contains an implementation of SONiC Platform Base API and
# provides the platform information
#
#############################################################################

try:
from sonic_platform_base.platform_base import PlatformBase
from sonic_platform.chassis import Chassis
except ImportError as e:
raise ImportError(str(e) + "- required module not found")


class Platform(PlatformBase):
"""
DELLEMC Platform-specific class
"""

def __init__(self):
PlatformBase.__init__(self)
self._chassis = Chassis()
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
#!/usr/bin/env python

########################################################################
# DellEMC N3248PXE
#
# Module contains an implementation of SONiC Platform Base API and
# provides the PSUs' information which are available in the platform
#
########################################################################

try:
import os
from sonic_platform_base.psu_base import PsuBase
from sonic_platform.fan import Fan
except ImportError as e:
raise ImportError(str(e) + "- required module not found")


class Psu(PsuBase):
"""DellEMC Platform-specific PSU class"""

def __init__(self, psu_index):
PsuBase.__init__(self)
self.index = psu_index + 1 # PSU is 1-based in DellEMC platforms
self.psu_presence_reg = "psu{}_prs".format(psu_index)
self.psu_status = "psu{}_status".format(psu_index)
self.eeprom = "/sys/bus/i2c/devices/{}-0056/eeprom".format(10+psu_index)
self.psu_voltage_reg = 'in3_input'
self.psu_current_reg = 'curr2_input'
self.psu_power_reg = 'power2_input'
self.dps_hwmon = "/sys/bus/i2c/devices/{}-005e/hwmon/".format(10 + psu_index)
self.dps_hwmon_exist = os.path.exists(self.dps_hwmon)
self._fan_list.append(Fan(fan_index=self.index, psu_fan=True, dependency=self))

def _get_cpld_register(self, reg_name):
# On successful read, returns the value read from given
# reg name and on failure rethrns 'ERR'
cpld_dir = "/sys/devices/platform/dell-n3248pxe-cpld.0/"
cpld_reg_file = cpld_dir + '/' + reg_name
try:
rv = open(cpld_reg_file, 'r').read()
except IOError : return 'ERR'
return rv.strip('\r\n').lstrip(' ')

def _get_dps_register(self, reg_name):
try :
dps_dir = self.dps_hwmon + '/' + os.listdir(self.dps_hwmon)[0]
dps_reg_file = dps_dir + '/' + reg_name
rv = open(dps_reg_file, 'r').read()
except (IOError, OSError) : return 'ERR'
return rv

def get_name(self):
"""
Retrieves the name of the device
Returns:
string: The name of the device
"""
return "PSU{}".format(self.index)

def _reload_dps_module(self):
try:
del_cmd = "echo 0x56 > /sys/bus/i2c/devices/i2c-{}/delete_device".format(10 + self.index - 1)
os.system(del_cmd)
except (IOError, OSError):
pass
try:
del_cmd = "echo 0x5e > /sys/bus/i2c/devices/i2c-{}/delete_device".format(10 + self.index - 1)
os.system(del_cmd)
except (IOError, OSError):
pass
try:
ins_cmd = "echo '24c02 0x56' > /sys/bus/i2c/devices/i2c-{}/new_device".format(10 + self.index - 1)
os.system(ins_cmd)
ins_cmd = "echo 'dps460 0x5e' > /sys/bus/i2c/devices/i2c-{}/new_device".format(10 + self.index - 1)
os.system(ins_cmd)
except (IOError, OSError):
pass

def get_presence(self):
"""
Retrieves the presence of the Power Supply Unit (PSU)
Returns:
bool: True if PSU is present, False if not
"""
presence = self._get_cpld_register(self.psu_presence_reg).strip()
if presence == 'ERR' : return False
if not self.dps_hwmon_exist and int(presence, 0):
self.dps_hwmon_exist = os.path.exists(self.dps_hwmon)
if not self.dps_hwmon_exist:
self._reload_dps_module()
return int(presence, 0)

def get_model(self):
"""
Retrieves the part number of the PSU
Returns:
string: Part number of PSU
"""
try: val = open(self.eeprom, "rb").read()[0x50:0x62]
except Exception:
val = None
return val.decode()

def get_serial(self):
"""
Retrieves the serial number of the PSU
Returns:
string: Serial number of PSU
"""
try: val = open(self.eeprom, "rb").read()[0xc4:0xd9]
except Exception:
val = None
return val.decode()

def get_status(self):
"""
Retrieves the operational status of the PSU
Returns:
bool: True if PSU is operating properly, False if not
"""
status = self._get_cpld_register(self.psu_status).strip()
if status == 'ERR' : return False
return int(status, 0)

def get_voltage(self):
"""
Retrieves current PSU voltage output
Returns:
A float number, the output voltage in volts,
e.g. 12.1
"""
volt_reading = self._get_dps_register(self.psu_voltage_reg)
try:
voltage = int(volt_reading)/1000
except Exception:
return None
return "{:.1f}".format(voltage)

def get_current(self):
"""
Retrieves present electric current supplied by PSU
Returns:
A float number, electric current in amperes,
e.g. 15.4
"""
curr_reading = self._get_dps_register(self.psu_current_reg)
try:
current = int(curr_reading)/1000
except Exception:
return None
return "{:.1f}".format(current)

def get_power(self):
"""
Retrieves current energy supplied by PSU
Returns:
A float number, the power in watts,
e.g. 302.6
"""
power_reading = self._get_dps_register(self.psu_power_reg)
try:
power = int(power_reading)/1000
except Exception:
return None
return "{:.1f}".format(power)

def get_powergood_status(self):
"""
Retrieves the powergood status of PSU
Returns:
A boolean, True if PSU has stablized its output voltages and
passed all its internal self-tests, False if not.
"""
power_good = self._get_cpld_register(self.psu_status).strip()
if power_good == 'ERR' : return False
return int(power_good, 0)

def get_mfr_id(self):
"""
Retrives the Manufacturer Id of PSU
Returns:
A string, the manunfacturer id.
"""
return 'DELTA'

def get_type(self):
"""
Retrives the Power Type of PSU
Returns :
A string, PSU power type
"""
try: val = open(self.eeprom, "rb").read()[0xe8:0xea]
except Exception:
return None
return val.decode()
Loading

0 comments on commit b0b0ba8

Please sign in to comment.