From 0c09de97de1fa2d0f3cf15609072ba3e48e1f3aa Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sun, 7 Apr 2024 21:23:46 -0500 Subject: [PATCH 1/3] CEL_DS3000 add cel ds3000 platform code --- .../DS3000/buffers.json.j2 | 2 + .../DS3000/buffers_defaults_def.j2 | 46 + .../DS3000/buffers_defaults_t0.j2 | 45 + .../DS3000/buffers_defaults_t1.j2 | 46 + .../x86_64-cel_ds3000-r0/DS3000/hwsku.json | 169 ++ .../x86_64-cel_ds3000-r0/DS3000/l2/config | 3 + .../x86_64-cel_ds3000-r0/DS3000/l3/config | 3 + .../DS3000/pg_profile_lookup.ini | 17 + .../DS3000/platform-def.json | 26 + .../DS3000/port_config.ini | 34 + .../x86_64-cel_ds3000-r0/DS3000/qos.json.j2 | 1 + .../x86_64-cel_ds3000-r0/DS3000/sai.profile | 2 + .../DS3000/td3-ds3000-32x100G.config.bcm | 580 ++++ .../x86_64-cel_ds3000-r0/custom_led.bin | Bin 0 -> 130 bytes .../x86_64-cel_ds3000-r0/default_sku | 1 + .../x86_64-cel_ds3000-r0/installer.conf | 4 + .../x86_64-cel_ds3000-r0/led_proc_init.soc | 2 + .../celestica/x86_64-cel_ds3000-r0/pcie.yaml | 165 ++ .../x86_64-cel_ds3000-r0/pddf/pd-plugin.json | 91 + .../pddf/pddf-device-bmc.json | 2125 +++++++++++++++ .../pddf/pddf-device-nonbmc.json | 2216 +++++++++++++++ .../x86_64-cel_ds3000-r0/pddf_support | 0 .../x86_64-cel_ds3000-r0/platform.json | 559 ++++ .../x86_64-cel_ds3000-r0/platform_asic | 1 + .../platform_components.json | 17 + .../pmon_daemon_control.json | 7 + .../x86_64-cel_ds3000-r0/sensors.conf | 87 + .../system_health_monitoring_config.json | 14 + .../x86_64-cel_ds3000-r0/thermal_policy.json | 136 + .../debian/platform-modules-ds3000.install | 8 + .../debian/platform-modules-ds3000.postinst | 4 + .../ds3000/modules/Makefile | 8 + .../ds3000/modules/baseboard_cpld.c | 414 +++ .../modules/led_driver/pddf_custom_led_defs.h | 149 ++ .../led_driver/pddf_custom_led_module.c | 873 ++++++ .../ds3000/modules/lm75.c | 958 +++++++ .../ds3000/modules/lm75.h | 41 + .../ds3000/modules/mc24lc64t.c | 172 ++ .../ds3000/modules/pddf_custom_fpga_algo.c | 626 +++++ .../ds3000/modules/pddf_custom_fpga_extend.c | 372 +++ .../ds3000/modules/psu_driver/pddf_psu_api.c | 478 ++++ .../ds3000/modules/psu_driver/pddf_psu_api.h | 31 + .../ds3000/modules/psu_driver/pddf_psu_defs.h | 90 + .../modules/psu_driver/pddf_psu_driver.c | 398 +++ .../modules/psu_driver/pddf_psu_driver.h | 70 + .../ds3000/modules/switchboard_fpga.c | 2365 +++++++++++++++++ .../ds3000/pddf/setup.py | 28 + .../ds3000/pddf/sonic_platform/__init__.py | 4 + .../ds3000/pddf/sonic_platform/chassis.py | 283 ++ .../ds3000/pddf/sonic_platform/component.py | 146 + .../pddf/sonic_platform/cpld_watchdog.py | 228 ++ .../pddf/sonic_platform/custom_component.py | 198 ++ .../ds3000/pddf/sonic_platform/eeprom.py | 75 + .../ds3000/pddf/sonic_platform/event.py | 52 + .../ds3000/pddf/sonic_platform/fan.py | 172 ++ .../ds3000/pddf/sonic_platform/fan_drawer.py | 25 + .../ds3000/pddf/sonic_platform/helper.py | 130 + .../ds3000/pddf/sonic_platform/pcie.py | 10 + .../ds3000/pddf/sonic_platform/platform.py | 23 + .../ds3000/pddf/sonic_platform/psu.py | 72 + .../ds3000/pddf/sonic_platform/sfp.py | 94 + .../ds3000/pddf/sonic_platform/thermal.py | 98 + .../pddf/sonic_platform/thermal_actions.py | 341 +++ .../pddf/sonic_platform/thermal_conditions.py | 121 + .../pddf/sonic_platform/thermal_infos.py | 362 +++ .../pddf/sonic_platform/thermal_manager.py | 21 + .../ds3000/pddf/sonic_platform/watchdog.py | 14 + .../ds3000/scripts/pddf_post_device_create.sh | 21 + .../ds3000/scripts/pddf_pre_driver_install.sh | 11 + .../ds3000/scripts/pre_pddf_init.sh | 30 + .../ds3000/systemd/pddf-platform-init.service | 16 + .../ds3000/utils/afulnx_64 | Bin 0 -> 1134256 bytes .../ds3000/utils/fpga_prog | Bin 0 -> 17536 bytes .../ds3000/utils/ispvm | Bin 0 -> 101752 bytes .../ds3000/utils/pddf_switch_svc.py | 83 + 75 files changed, 16114 insertions(+) create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers.json.j2 create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers_defaults_def.j2 create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers_defaults_t0.j2 create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers_defaults_t1.j2 create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/hwsku.json create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/l2/config create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/l3/config create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/pg_profile_lookup.ini create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/platform-def.json create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/port_config.ini create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/qos.json.j2 create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/sai.profile create mode 100644 device/celestica/x86_64-cel_ds3000-r0/DS3000/td3-ds3000-32x100G.config.bcm create mode 100644 device/celestica/x86_64-cel_ds3000-r0/custom_led.bin create mode 100644 device/celestica/x86_64-cel_ds3000-r0/default_sku create mode 100644 device/celestica/x86_64-cel_ds3000-r0/installer.conf create mode 100644 device/celestica/x86_64-cel_ds3000-r0/led_proc_init.soc create mode 100644 device/celestica/x86_64-cel_ds3000-r0/pcie.yaml create mode 100644 device/celestica/x86_64-cel_ds3000-r0/pddf/pd-plugin.json create mode 100644 device/celestica/x86_64-cel_ds3000-r0/pddf/pddf-device-bmc.json create mode 100644 device/celestica/x86_64-cel_ds3000-r0/pddf/pddf-device-nonbmc.json create mode 100644 device/celestica/x86_64-cel_ds3000-r0/pddf_support create mode 100644 device/celestica/x86_64-cel_ds3000-r0/platform.json create mode 100644 device/celestica/x86_64-cel_ds3000-r0/platform_asic create mode 100644 device/celestica/x86_64-cel_ds3000-r0/platform_components.json create mode 100644 device/celestica/x86_64-cel_ds3000-r0/pmon_daemon_control.json create mode 100644 device/celestica/x86_64-cel_ds3000-r0/sensors.conf create mode 100644 device/celestica/x86_64-cel_ds3000-r0/system_health_monitoring_config.json create mode 100644 device/celestica/x86_64-cel_ds3000-r0/thermal_policy.json create mode 100644 platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-ds3000.install create mode 100644 platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-ds3000.postinst create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/Makefile create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/baseboard_cpld.c create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/led_driver/pddf_custom_led_defs.h create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/led_driver/pddf_custom_led_module.c create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/lm75.c create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/lm75.h create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/mc24lc64t.c create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/pddf_custom_fpga_algo.c create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/pddf_custom_fpga_extend.c create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_api.c create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_api.h create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_defs.h create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_driver.c create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_driver.h create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/modules/switchboard_fpga.c create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/setup.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/__init__.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/chassis.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/component.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/cpld_watchdog.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/custom_component.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/eeprom.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/event.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/fan.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/fan_drawer.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/helper.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/pcie.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/platform.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/psu.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/sfp.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_actions.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_conditions.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_infos.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_manager.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/watchdog.py create mode 100755 platform/broadcom/sonic-platform-modules-cel/ds3000/scripts/pddf_post_device_create.sh create mode 100755 platform/broadcom/sonic-platform-modules-cel/ds3000/scripts/pddf_pre_driver_install.sh create mode 100755 platform/broadcom/sonic-platform-modules-cel/ds3000/scripts/pre_pddf_init.sh create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/systemd/pddf-platform-init.service create mode 100755 platform/broadcom/sonic-platform-modules-cel/ds3000/utils/afulnx_64 create mode 100755 platform/broadcom/sonic-platform-modules-cel/ds3000/utils/fpga_prog create mode 100755 platform/broadcom/sonic-platform-modules-cel/ds3000/utils/ispvm create mode 100644 platform/broadcom/sonic-platform-modules-cel/ds3000/utils/pddf_switch_svc.py diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers.json.j2 b/device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers.json.j2 new file mode 100644 index 000000000000..0b1cb2c541b6 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers.json.j2 @@ -0,0 +1,2 @@ +{%- set default_topo = 't1' %} +{%- include 'buffers_config.j2' %} diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers_defaults_def.j2 b/device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers_defaults_def.j2 new file mode 100644 index 000000000000..740cfdf79e96 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers_defaults_def.j2 @@ -0,0 +1,46 @@ +{%- 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": { + "xoff": "4625920", + "size": "12766208", + "type": "ingress", + "mode": "dynamic" + }, + "egress_lossless_pool": { + "size": "12766208", + "type": "egress", + "mode": "static" + }, + "egress_lossy_pool": { + "size": "7326924", + "type": "egress", + "mode": "dynamic" + } + }, + "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":"12766208" + }, + "egress_lossy_profile": { + "pool":"[BUFFER_POOL|egress_lossless_pool]", + "size":"1518", + "dynamic_th":"3" + } + }, +{%- endmacro %} diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers_defaults_t0.j2 b/device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers_defaults_t0.j2 new file mode 100644 index 000000000000..44fcf21887a6 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers_defaults_t0.j2 @@ -0,0 +1,45 @@ +{%- 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": "12766208", + "type": "ingress", + "mode": "dynamic" + }, + "egress_lossless_pool": { + "size": "12766208", + "type": "egress", + "mode": "static" + }, + "egress_lossy_pool": { + "size": "7326924", + "type": "egress", + "mode": "dynamic" + } + }, + "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":"12766208" + }, + "egress_lossy_profile": { + "pool":"[BUFFER_POOL|egress_lossless_pool]", + "size":"1518", + "dynamic_th":"3" + } + }, +{%- endmacro %} diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers_defaults_t1.j2 b/device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers_defaults_t1.j2 new file mode 100644 index 000000000000..740cfdf79e96 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/buffers_defaults_t1.j2 @@ -0,0 +1,46 @@ +{%- 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": { + "xoff": "4625920", + "size": "12766208", + "type": "ingress", + "mode": "dynamic" + }, + "egress_lossless_pool": { + "size": "12766208", + "type": "egress", + "mode": "static" + }, + "egress_lossy_pool": { + "size": "7326924", + "type": "egress", + "mode": "dynamic" + } + }, + "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":"12766208" + }, + "egress_lossy_profile": { + "pool":"[BUFFER_POOL|egress_lossless_pool]", + "size":"1518", + "dynamic_th":"3" + } + }, +{%- endmacro %} diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/hwsku.json b/device/celestica/x86_64-cel_ds3000-r0/DS3000/hwsku.json new file mode 100644 index 000000000000..28e50b8c0385 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/hwsku.json @@ -0,0 +1,169 @@ +{ + "interfaces": { + "Ethernet0": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet4": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet8": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet12": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet16": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet20": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet24": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet28": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet32": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet36": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet40": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet44": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet48": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet52": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet56": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet60": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet64": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet68": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet72": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet76": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet80": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet84": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet88": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet92": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet96": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet100": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet104": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet108": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet112": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet116": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet120": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet124": { + "default_brkout_mode": "1x100G", + "autoneg": "off", + "fec": "rs" + }, + "Ethernet128": { + "default_brkout_mode": "1x10G", + "autoneg": "off", + "fec": "none" + } + } +} diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/l2/config b/device/celestica/x86_64-cel_ds3000-r0/DS3000/l2/config new file mode 100644 index 000000000000..45a7b84d5032 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/l2/config @@ -0,0 +1,3 @@ +l2_mem_entries=139264 +l3_mem_entries=8192 +l3_alpm_enable=0 diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/l3/config b/device/celestica/x86_64-cel_ds3000-r0/DS3000/l3/config new file mode 100644 index 000000000000..3467c1b39716 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/l3/config @@ -0,0 +1,3 @@ +l2_mem_entries=40000 +l3_mem_entries=40000 +l3_alpm_enable=2 diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/pg_profile_lookup.ini b/device/celestica/x86_64-cel_ds3000-r0/DS3000/pg_profile_lookup.ini new file mode 100644 index 000000000000..9f2eacb6fc42 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/pg_profile_lookup.ini @@ -0,0 +1,17 @@ +# PG lossless profiles. +# speed cable size xon xoff threshold xon_offset + 10000 5m 56368 18432 55120 -3 2496 + 25000 5m 56368 18432 55120 -3 2496 + 40000 5m 56368 18432 55120 -3 2496 + 50000 5m 56368 18432 55120 -3 2496 + 100000 5m 56368 18432 55120 -3 2496 + 10000 40m 56368 18432 55120 -3 2496 + 25000 40m 56368 18432 55120 -3 2496 + 40000 40m 56368 18432 55120 -3 2496 + 50000 40m 56368 18432 55120 -3 2496 + 100000 40m 56368 18432 55120 -3 2496 + 10000 300m 56368 18432 55120 -3 2496 + 25000 300m 56368 18432 55120 -3 2496 + 40000 300m 56368 18432 55120 -3 2496 + 50000 300m 56368 18432 55120 -3 2496 + 100000 300m 56368 18432 55120 -3 2496 diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/platform-def.json b/device/celestica/x86_64-cel_ds3000-r0/DS3000/platform-def.json new file mode 100644 index 000000000000..1b3dbaa9c4b2 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/platform-def.json @@ -0,0 +1,26 @@ +{ + "fec-mode": { + "Ethernet0-127": { + "1": { + "10000": [ "none", "fc" ], + "25000": [ "none", "fc" ] + }, + "2": { + "20000": [ "none", "fc" ], + "50000": [ "none", "rs" ] + }, + "4": { + "40000": [ "none", "fc" ], + "100000": [ "none", "rs" ] + } + } + }, + "native-port-supported-speeds": { + "Ethernet0-127": { + "4": ["100000","40000"] + }, + "Ethernet128-128": { + "1": ["100000","1000"] + } + } +} diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/port_config.ini b/device/celestica/x86_64-cel_ds3000-r0/DS3000/port_config.ini new file mode 100644 index 000000000000..ce9e5a2c1f85 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/port_config.ini @@ -0,0 +1,34 @@ +# name lanes alias index speed valid_speeds +Ethernet0 1,2,3,4 QSFP1 1 100000 100000,40000 +Ethernet4 5,6,7,8 QSFP2 2 100000 100000,40000 +Ethernet8 9,10,11,12 QSFP3 3 100000 100000,40000 +Ethernet12 13,14,15,16 QSFP4 4 100000 100000,40000 +Ethernet16 17,18,19,20 QSFP5 5 100000 100000,40000 +Ethernet20 21,22,23,24 QSFP6 6 100000 100000,40000 +Ethernet24 25,26,27,28 QSFP7 7 100000 100000,40000 +Ethernet28 29,30,31,32 QSFP8 8 100000 100000,40000 +Ethernet32 33,34,35,36 QSFP9 9 100000 100000,40000 +Ethernet36 37,38,39,40 QSFP10 10 100000 100000,40000 +Ethernet40 41,42,43,44 QSFP11 11 100000 100000,40000 +Ethernet44 45,46,47,48 QSFP12 12 100000 100000,40000 +Ethernet48 49,50,51,52 QSFP13 13 100000 100000,40000 +Ethernet52 53,54,55,56 QSFP14 14 100000 100000,40000 +Ethernet56 57,58,59,60 QSFP15 15 100000 100000,40000 +Ethernet60 61,62,63,64 QSFP16 16 100000 100000,40000 +Ethernet64 65,66,67,68 QSFP17 17 100000 100000,40000 +Ethernet68 69,70,71,72 QSFP18 18 100000 100000,40000 +Ethernet72 73,74,75,76 QSFP19 19 100000 100000,40000 +Ethernet76 77,78,79,80 QSFP20 20 100000 100000,40000 +Ethernet80 81,82,83,84 QSFP21 21 100000 100000,40000 +Ethernet84 85,86,87,88 QSFP22 22 100000 100000,40000 +Ethernet88 89,90,91,92 QSFP23 23 100000 100000,40000 +Ethernet92 93,94,95,96 QSFP24 24 100000 100000,40000 +Ethernet96 97,98,99,100 QSFP25 25 100000 100000,40000 +Ethernet100 101,102,103,104 QSFP26 26 100000 100000,40000 +Ethernet104 105,106,107,108 QSFP27 27 100000 100000,40000 +Ethernet108 109,110,111,112 QSFP28 28 100000 100000,40000 +Ethernet112 113,114,115,116 QSFP29 29 100000 100000,40000 +Ethernet116 117,118,119,120 QSFP30 30 100000 100000,40000 +Ethernet120 121,122,123,124 QSFP31 31 100000 100000,40000 +Ethernet124 125,126,127,128 QSFP32 32 100000 100000,40000 +Ethernet128 129 SFP1 33 10000 10000,1000 diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/qos.json.j2 b/device/celestica/x86_64-cel_ds3000-r0/DS3000/qos.json.j2 new file mode 100644 index 000000000000..3e548325ea30 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/qos.json.j2 @@ -0,0 +1 @@ +{%- include 'qos_config.j2' %} diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/sai.profile b/device/celestica/x86_64-cel_ds3000-r0/DS3000/sai.profile new file mode 100644 index 000000000000..992e6ba1f49a --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/sai.profile @@ -0,0 +1,2 @@ +SAI_INIT_CONFIG_FILE=/usr/share/sonic/hwsku/td3-ds3000-32x100G.config.bcm +SAI_NUM_ECMP_MEMBERS=64 diff --git a/device/celestica/x86_64-cel_ds3000-r0/DS3000/td3-ds3000-32x100G.config.bcm b/device/celestica/x86_64-cel_ds3000-r0/DS3000/td3-ds3000-32x100G.config.bcm new file mode 100644 index 000000000000..943a47dea5e7 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/DS3000/td3-ds3000-32x100G.config.bcm @@ -0,0 +1,580 @@ +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=32768 +l3_alpm_ipv6_128b_bkt_rsvd=1 +l3_alpm_enable=2 +#lpm_scaling_enable=1 +max_vp_lags=0 +#mem_cache_enable=0 +memlist_enable=1 +reglist_enable=1 +#scache_filename=/tmp/scache +schan_intr_enable=0 +stable_size=0x5500000 +tdma_timeout_usec=3000000 +miim_intr_enable=0 +module_64ports=1 +oversubscribe_mode=1 +parity_enable=0 +serdes_lane_config_dfe=on +#serdes_fec_enable=1 +serdes_if_type_ce=14 +pbmp_gport_stack.0=0x0000000000000000000000000000000000000000000000000000000000000000 +pbmp_xport_xe=0x3ffffffffffffffffffffffffffffffffe +port_flex_enable=1 +fpem_mem_entries=32768 + +#Tunnels +bcm_tunnel_term_compatible_mode=1 +sai_tunnel_support=1 +use_all_splithorizon_groups=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 +flow_init_mode=1 + +ptp_ts_pll_fref=50000000 +ptp_bs_fref_0=50000000 +ptp_bs_fref_1=50000000 +phy_an_c73=1 + +portmap_1=1:100 +portmap_5=5:100 +portmap_9=9:100 +portmap_13=13:100 +portmap_17=17:100 +portmap_21=21:100 +portmap_25=25:100 +portmap_29=29:100 +portmap_33=33:100 +portmap_37=37:100 +portmap_41=41:100 +portmap_45=45:100 +portmap_49=49:100 +portmap_53=53:100 +portmap_57=57:100 +portmap_61=61:100 +portmap_67=65:100 +portmap_71=69:100 +portmap_75=73:100 +portmap_79=77:100 +portmap_83=81:100 +portmap_87=85:100 +portmap_91=89:100 +portmap_95=93:100 +portmap_99=97:100 +portmap_103=101:100 +portmap_107=105:100 +portmap_111=109:100 +portmap_115=113:100 +portmap_119=117:100 +portmap_123=121:100 +portmap_127=125:100 +portmap_66=129:10:m +#portmap_130=128:10:m + +#wc0 lane swap +phy_chain_tx_lane_map_physical{1.0}=0x0132 +phy_chain_rx_lane_map_physical{1.0}=0x3210 + +#wc1 lane swap +phy_chain_tx_lane_map_physical{5.0}=0x2301 +phy_chain_rx_lane_map_physical{5.0}=0x2031 + +#wc2 lane swap +phy_chain_tx_lane_map_physical{9.0}=0x0132 +phy_chain_rx_lane_map_physical{9.0}=0x3210 + +#wc3 lane swap +phy_chain_tx_lane_map_physical{13.0}=0x3201 +phy_chain_rx_lane_map_physical{13.0}=0x2031 + +#wc4 lane swap +phy_chain_tx_lane_map_physical{17.0}=0x0123 +phy_chain_rx_lane_map_physical{17.0}=0x3210 + +#wc5 lane swap +phy_chain_tx_lane_map_physical{21.0}=0x2301 +phy_chain_rx_lane_map_physical{21.0}=0x2031 + +#wc6 lane swap +phy_chain_tx_lane_map_physical{25.0}=0x0123 +phy_chain_rx_lane_map_physical{25.0}=0x3210 + +#wc7 lane swap +phy_chain_tx_lane_map_physical{29.0}=0x3201 +phy_chain_rx_lane_map_physical{29.0}=0x2031 + +#wc8 lane swap +phy_chain_tx_lane_map_physical{33.0}=0x0213 +phy_chain_rx_lane_map_physical{33.0}=0x1302 + +#wc9 lane swap +phy_chain_tx_lane_map_physical{37.0}=0x1302 +phy_chain_rx_lane_map_physical{37.0}=0x2031 + +#wc10 lane swap +phy_chain_tx_lane_map_physical{41.0}=0x0231 +phy_chain_rx_lane_map_physical{41.0}=0x3120 + +#wc11 lane swap +phy_chain_tx_lane_map_physical{45.0}=0x1302 +phy_chain_rx_lane_map_physical{45.0}=0x2031 + +#wc12 lane swap +phy_chain_tx_lane_map_physical{49.0}=0x2103 +phy_chain_rx_lane_map_physical{49.0}=0x3120 + +#wc13 lane swap +phy_chain_tx_lane_map_physical{53.0}=0x2301 +phy_chain_rx_lane_map_physical{53.0}=0x2031 + +#wc14 lane swap +phy_chain_tx_lane_map_physical{57.0}=0x0123 +phy_chain_rx_lane_map_physical{57.0}=0x2301 + +#wc15 lane swap +phy_chain_tx_lane_map_physical{61.0}=0x3210 +phy_chain_rx_lane_map_physical{61.0}=0x1032 + +#wc16 lane swap +phy_chain_tx_lane_map_physical{65.0}=0x3210 +phy_chain_rx_lane_map_physical{65.0}=0x1023 + +#wc17 lane swap +phy_chain_tx_lane_map_physical{69.0}=0x0123 +phy_chain_rx_lane_map_physical{69.0}=0x1302 + +#wc18 lane swap +phy_chain_tx_lane_map_physical{73.0}=0x2301 +phy_chain_rx_lane_map_physical{73.0}=0x1032 + +#wc19 lane swap +phy_chain_tx_lane_map_physical{77.0}=0x2013 +phy_chain_rx_lane_map_physical{77.0}=0x3120 + +#wc20 lane swap +phy_chain_tx_lane_map_physical{81.0}=0x1302 +phy_chain_rx_lane_map_physical{81.0}=0x2031 + +#wc21 lane swap +phy_chain_tx_lane_map_physical{85.0}=0x0123 +phy_chain_rx_lane_map_physical{85.0}=0x2130 + +#wc22 lane swap +phy_chain_tx_lane_map_physical{89.0}=0x2301 +phy_chain_rx_lane_map_physical{89.0}=0x2031 + +#wc23 lane swap +phy_chain_tx_lane_map_physical{93.0}=0x0312 +phy_chain_rx_lane_map_physical{93.0}=0x2310 + +#wc24 lane swap +phy_chain_tx_lane_map_physical{97.0}=0x2301 +phy_chain_rx_lane_map_physical{97.0}=0x1032 + +#wc25 lane swap +phy_chain_tx_lane_map_physical{101.0}=0x0123 +phy_chain_rx_lane_map_physical{101.0}=0x3210 + +#wc26 lane swap +phy_chain_tx_lane_map_physical{105.0}=0x2301 +phy_chain_rx_lane_map_physical{105.0}=0x1032 + +#wc27 lane swap +phy_chain_tx_lane_map_physical{109.0}=0x0123 +phy_chain_rx_lane_map_physical{109.0}=0x3210 + +#wc28 lane swap +phy_chain_tx_lane_map_physical{113.0}=0x2301 +phy_chain_rx_lane_map_physical{113.0}=0x2031 + +#wc29 lane swap +phy_chain_tx_lane_map_physical{117.0}=0x0123 +phy_chain_rx_lane_map_physical{117.0}=0x3210 + +#wc30 lane swap +phy_chain_tx_lane_map_physical{121.0}=0x2301 +phy_chain_rx_lane_map_physical{121.0}=0x1032 + +#wc31 lane swap +phy_chain_tx_lane_map_physical{125.0}=0x0123 +phy_chain_rx_lane_map_physical{125.0}=0x3210 + +#MC lane swap +phy_chain_tx_lane_map_physical{129.0}=0x3210 +phy_chain_rx_lane_map_physical{129.0}=0x3210 + + +#wc0 P/N flip +phy_chain_tx_polarity_flip_physical{1.0}=0x0 +phy_chain_rx_polarity_flip_physical{1.0}=0x0 +phy_chain_tx_polarity_flip_physical{2.0}=0x0 +phy_chain_rx_polarity_flip_physical{2.0}=0x1 +phy_chain_tx_polarity_flip_physical{3.0}=0x0 +phy_chain_rx_polarity_flip_physical{3.0}=0x0 +phy_chain_tx_polarity_flip_physical{4.0}=0x1 +phy_chain_rx_polarity_flip_physical{4.0}=0x1 + +#wc1 P/N flip +phy_chain_tx_polarity_flip_physical{5.0}=0x0 +phy_chain_rx_polarity_flip_physical{5.0}=0x0 +phy_chain_tx_polarity_flip_physical{6.0}=0x1 +phy_chain_rx_polarity_flip_physical{6.0}=0x1 +phy_chain_tx_polarity_flip_physical{7.0}=0x0 +phy_chain_rx_polarity_flip_physical{7.0}=0x1 +phy_chain_tx_polarity_flip_physical{8.0}=0x1 +phy_chain_rx_polarity_flip_physical{8.0}=0x1 + +#wc2 P/N flip +phy_chain_tx_polarity_flip_physical{9.0}=0x0 +phy_chain_rx_polarity_flip_physical{9.0}=0x0 +phy_chain_tx_polarity_flip_physical{10.0}=0x0 +phy_chain_rx_polarity_flip_physical{10.0}=0x1 +phy_chain_tx_polarity_flip_physical{11.0}=0x0 +phy_chain_rx_polarity_flip_physical{11.0}=0x0 +phy_chain_tx_polarity_flip_physical{12.0}=0x1 +phy_chain_rx_polarity_flip_physical{12.0}=0x1 + +#wc3 P/N flip +phy_chain_tx_polarity_flip_physical{13.0}=0x0 +phy_chain_rx_polarity_flip_physical{13.0}=0x0 +phy_chain_tx_polarity_flip_physical{14.0}=0x1 +phy_chain_rx_polarity_flip_physical{14.0}=0x1 +phy_chain_tx_polarity_flip_physical{15.0}=0x0 +phy_chain_rx_polarity_flip_physical{15.0}=0x1 +phy_chain_tx_polarity_flip_physical{16.0}=0x0 +phy_chain_rx_polarity_flip_physical{16.0}=0x1 + +#wc4 P/N flip +phy_chain_tx_polarity_flip_physical{17.0}=0x0 +phy_chain_rx_polarity_flip_physical{17.0}=0x0 +phy_chain_tx_polarity_flip_physical{18.0}=0x1 +phy_chain_rx_polarity_flip_physical{18.0}=0x1 +phy_chain_tx_polarity_flip_physical{19.0}=0x0 +phy_chain_rx_polarity_flip_physical{19.0}=0x0 +phy_chain_tx_polarity_flip_physical{20.0}=0x1 +phy_chain_rx_polarity_flip_physical{20.0}=0x1 + +#wc5 P/N flip +phy_chain_tx_polarity_flip_physical{21.0}=0x0 +phy_chain_rx_polarity_flip_physical{21.0}=0x0 +phy_chain_tx_polarity_flip_physical{22.0}=0x1 +phy_chain_rx_polarity_flip_physical{22.0}=0x1 +phy_chain_tx_polarity_flip_physical{23.0}=0x0 +phy_chain_rx_polarity_flip_physical{23.0}=0x1 +phy_chain_tx_polarity_flip_physical{24.0}=0x1 +phy_chain_rx_polarity_flip_physical{24.0}=0x1 + +#wc6 P/N flip +phy_chain_tx_polarity_flip_physical{25.0}=0x0 +phy_chain_rx_polarity_flip_physical{25.0}=0x1 +phy_chain_tx_polarity_flip_physical{26.0}=0x1 +phy_chain_rx_polarity_flip_physical{26.0}=0x0 +phy_chain_tx_polarity_flip_physical{27.0}=0x0 +phy_chain_rx_polarity_flip_physical{27.0}=0x1 +phy_chain_tx_polarity_flip_physical{28.0}=0x1 +phy_chain_rx_polarity_flip_physical{28.0}=0x0 + +#wc7 P/N flip +phy_chain_tx_polarity_flip_physical{29.0}=0x1 +phy_chain_rx_polarity_flip_physical{29.0}=0x1 +phy_chain_tx_polarity_flip_physical{30.0}=0x1 +phy_chain_rx_polarity_flip_physical{30.0}=0x0 +phy_chain_tx_polarity_flip_physical{31.0}=0x0 +phy_chain_rx_polarity_flip_physical{31.0}=0x0 +phy_chain_tx_polarity_flip_physical{32.0}=0x0 +phy_chain_rx_polarity_flip_physical{32.0}=0x0 + +#wc8 P/N flip +phy_chain_tx_polarity_flip_physical{33.0}=0x1 +phy_chain_rx_polarity_flip_physical{33.0}=0x1 +phy_chain_tx_polarity_flip_physical{34.0}=0x0 +phy_chain_rx_polarity_flip_physical{34.0}=0x0 +phy_chain_tx_polarity_flip_physical{35.0}=0x0 +phy_chain_rx_polarity_flip_physical{35.0}=0x0 +phy_chain_tx_polarity_flip_physical{36.0}=0x1 +phy_chain_rx_polarity_flip_physical{36.0}=0x0 + +#wc9 P/N flip +phy_chain_tx_polarity_flip_physical{37.0}=0x1 +phy_chain_rx_polarity_flip_physical{37.0}=0x1 +phy_chain_tx_polarity_flip_physical{38.0}=0x1 +phy_chain_rx_polarity_flip_physical{38.0}=0x0 +phy_chain_tx_polarity_flip_physical{39.0}=0x1 +phy_chain_rx_polarity_flip_physical{39.0}=0x0 +phy_chain_tx_polarity_flip_physical{40.0}=0x0 +phy_chain_rx_polarity_flip_physical{40.0}=0x1 + +#wc10 P/N flip +phy_chain_tx_polarity_flip_physical{41.0}=0x1 +phy_chain_rx_polarity_flip_physical{41.0}=0x1 +phy_chain_tx_polarity_flip_physical{42.0}=0x0 +phy_chain_rx_polarity_flip_physical{42.0}=0x1 +phy_chain_tx_polarity_flip_physical{43.0}=0x1 +phy_chain_rx_polarity_flip_physical{43.0}=0x0 +phy_chain_tx_polarity_flip_physical{44.0}=0x1 +phy_chain_rx_polarity_flip_physical{44.0}=0x1 + +#wc11 P/N flip +phy_chain_tx_polarity_flip_physical{45.0}=0x1 +phy_chain_rx_polarity_flip_physical{45.0}=0x0 +phy_chain_tx_polarity_flip_physical{46.0}=0x1 +phy_chain_rx_polarity_flip_physical{46.0}=0x0 +phy_chain_tx_polarity_flip_physical{47.0}=0x1 +phy_chain_rx_polarity_flip_physical{47.0}=0x1 +phy_chain_tx_polarity_flip_physical{48.0}=0x0 +phy_chain_rx_polarity_flip_physical{48.0}=0x1 + +#wc12 P/N flip +phy_chain_tx_polarity_flip_physical{49.0}=0x1 +phy_chain_rx_polarity_flip_physical{49.0}=0x0 +phy_chain_tx_polarity_flip_physical{50.0}=0x1 +phy_chain_rx_polarity_flip_physical{50.0}=0x0 +phy_chain_tx_polarity_flip_physical{51.0}=0x0 +phy_chain_rx_polarity_flip_physical{51.0}=0x1 +phy_chain_tx_polarity_flip_physical{52.0}=0x1 +phy_chain_rx_polarity_flip_physical{52.0}=0x1 + +#wc13 P/N flip +phy_chain_tx_polarity_flip_physical{53.0}=0x0 +phy_chain_rx_polarity_flip_physical{53.0}=0x0 +phy_chain_tx_polarity_flip_physical{54.0}=0x1 +phy_chain_rx_polarity_flip_physical{54.0}=0x1 +phy_chain_tx_polarity_flip_physical{55.0}=0x0 +phy_chain_rx_polarity_flip_physical{55.0}=0x1 +phy_chain_tx_polarity_flip_physical{56.0}=0x1 +phy_chain_rx_polarity_flip_physical{56.0}=0x1 + +#wc14 P/N flip +phy_chain_tx_polarity_flip_physical{57.0}=0x1 +phy_chain_rx_polarity_flip_physical{57.0}=0x0 +phy_chain_tx_polarity_flip_physical{58.0}=0x1 +phy_chain_rx_polarity_flip_physical{58.0}=0x1 +phy_chain_tx_polarity_flip_physical{59.0}=0x0 +phy_chain_rx_polarity_flip_physical{59.0}=0x0 +phy_chain_tx_polarity_flip_physical{60.0}=0x1 +phy_chain_rx_polarity_flip_physical{60.0}=0x1 + +#wc15 P/N flip +phy_chain_tx_polarity_flip_physical{61.0}=0x0 +phy_chain_rx_polarity_flip_physical{61.0}=0x1 +phy_chain_tx_polarity_flip_physical{62.0}=0x1 +phy_chain_rx_polarity_flip_physical{62.0}=0x0 +phy_chain_tx_polarity_flip_physical{63.0}=0x0 +phy_chain_rx_polarity_flip_physical{63.0}=0x1 +phy_chain_tx_polarity_flip_physical{64.0}=0x0 +phy_chain_rx_polarity_flip_physical{64.0}=0x0 + +#wc16 P/N flip +phy_chain_tx_polarity_flip_physical{65.0}=0x1 +phy_chain_rx_polarity_flip_physical{65.0}=0x0 +phy_chain_tx_polarity_flip_physical{66.0}=0x0 +phy_chain_rx_polarity_flip_physical{66.0}=0x0 +phy_chain_tx_polarity_flip_physical{67.0}=0x1 +phy_chain_rx_polarity_flip_physical{67.0}=0x1 +phy_chain_tx_polarity_flip_physical{68.0}=0x0 +phy_chain_rx_polarity_flip_physical{68.0}=0x0 + +#wc17 P/N flip +phy_chain_tx_polarity_flip_physical{69.0}=0x1 +phy_chain_rx_polarity_flip_physical{69.0}=0x1 +phy_chain_tx_polarity_flip_physical{70.0}=0x0 +phy_chain_rx_polarity_flip_physical{70.0}=0x0 +phy_chain_tx_polarity_flip_physical{71.0}=0x1 +phy_chain_rx_polarity_flip_physical{71.0}=0x0 +phy_chain_tx_polarity_flip_physical{72.0}=0x0 +phy_chain_rx_polarity_flip_physical{72.0}=0x0 + +#wc18 P/N flip +phy_chain_tx_polarity_flip_physical{73.0}=0x0 +phy_chain_rx_polarity_flip_physical{73.0}=0x1 +phy_chain_tx_polarity_flip_physical{74.0}=0x1 +phy_chain_rx_polarity_flip_physical{74.0}=0x0 +phy_chain_tx_polarity_flip_physical{75.0}=0x0 +phy_chain_rx_polarity_flip_physical{75.0}=0x1 +phy_chain_tx_polarity_flip_physical{76.0}=0x1 +phy_chain_rx_polarity_flip_physical{76.0}=0x0 + +#wc19 P/N flip +phy_chain_tx_polarity_flip_physical{77.0}=0x0 +phy_chain_rx_polarity_flip_physical{77.0}=0x0 +phy_chain_tx_polarity_flip_physical{78.0}=0x0 +phy_chain_rx_polarity_flip_physical{78.0}=0x0 +phy_chain_tx_polarity_flip_physical{79.0}=0x1 +phy_chain_rx_polarity_flip_physical{79.0}=0x1 +phy_chain_tx_polarity_flip_physical{80.0}=0x1 +phy_chain_rx_polarity_flip_physical{80.0}=0x1 + +#wc20 P/N flip +phy_chain_tx_polarity_flip_physical{81.0}=0x0 +phy_chain_rx_polarity_flip_physical{81.0}=0x0 +phy_chain_tx_polarity_flip_physical{82.0}=0x0 +phy_chain_rx_polarity_flip_physical{82.0}=0x0 +phy_chain_tx_polarity_flip_physical{83.0}=0x1 +phy_chain_rx_polarity_flip_physical{83.0}=0x1 +phy_chain_tx_polarity_flip_physical{84.0}=0x1 +phy_chain_rx_polarity_flip_physical{84.0}=0x0 + +#wc21 P/N flip +phy_chain_tx_polarity_flip_physical{85.0}=0x1 +phy_chain_rx_polarity_flip_physical{85.0}=0x1 +phy_chain_tx_polarity_flip_physical{86.0}=0x0 +phy_chain_rx_polarity_flip_physical{86.0}=0x1 +phy_chain_tx_polarity_flip_physical{87.0}=0x1 +phy_chain_rx_polarity_flip_physical{87.0}=0x0 +phy_chain_tx_polarity_flip_physical{88.0}=0x0 +phy_chain_rx_polarity_flip_physical{88.0}=0x0 + +#wc22 P/N flip +phy_chain_tx_polarity_flip_physical{89.0}=0x1 +phy_chain_rx_polarity_flip_physical{89.0}=0x0 +phy_chain_tx_polarity_flip_physical{90.0}=0x0 +phy_chain_rx_polarity_flip_physical{90.0}=0x0 +phy_chain_tx_polarity_flip_physical{91.0}=0x1 +phy_chain_rx_polarity_flip_physical{91.0}=0x1 +phy_chain_tx_polarity_flip_physical{92.0}=0x0 +phy_chain_rx_polarity_flip_physical{92.0}=0x1 + +#wc23 P/N flip +phy_chain_tx_polarity_flip_physical{93.0}=0x1 +phy_chain_rx_polarity_flip_physical{93.0}=0x1 +phy_chain_tx_polarity_flip_physical{94.0}=0x1 +phy_chain_rx_polarity_flip_physical{94.0}=0x1 +phy_chain_tx_polarity_flip_physical{95.0}=0x0 +phy_chain_rx_polarity_flip_physical{95.0}=0x0 +phy_chain_tx_polarity_flip_physical{96.0}=0x0 +phy_chain_rx_polarity_flip_physical{96.0}=0x1 + +#wc24 P/N flip +phy_chain_tx_polarity_flip_physical{97.0}=0x1 +phy_chain_rx_polarity_flip_physical{97.0}=0x1 +phy_chain_tx_polarity_flip_physical{98.0}=0x0 +phy_chain_rx_polarity_flip_physical{98.0}=0x0 +phy_chain_tx_polarity_flip_physical{99.0}=0x1 +phy_chain_rx_polarity_flip_physical{99.0}=0x1 +phy_chain_tx_polarity_flip_physical{100.0}=0x0 +phy_chain_rx_polarity_flip_physical{100.0}=0x0 + +#wc25 P/N flip +phy_chain_tx_polarity_flip_physical{101.0}=0x1 +phy_chain_rx_polarity_flip_physical{101.0}=0x0 +phy_chain_tx_polarity_flip_physical{102.0}=0x0 +phy_chain_rx_polarity_flip_physical{102.0}=0x1 +phy_chain_tx_polarity_flip_physical{103.0}=0x1 +phy_chain_rx_polarity_flip_physical{103.0}=0x0 +phy_chain_tx_polarity_flip_physical{104.0}=0x0 +phy_chain_rx_polarity_flip_physical{104.0}=0x0 + +#wc26 P/N flip +phy_chain_tx_polarity_flip_physical{105.0}=0x1 +phy_chain_rx_polarity_flip_physical{105.0}=0x0 +phy_chain_tx_polarity_flip_physical{106.0}=0x0 +phy_chain_rx_polarity_flip_physical{106.0}=0x1 +phy_chain_tx_polarity_flip_physical{107.0}=0x1 +phy_chain_rx_polarity_flip_physical{107.0}=0x0 +phy_chain_tx_polarity_flip_physical{108.0}=0x0 +phy_chain_rx_polarity_flip_physical{108.0}=0x1 + +#wc27 P/N flip +phy_chain_tx_polarity_flip_physical{109.0}=0x1 +phy_chain_rx_polarity_flip_physical{109.0}=0x1 +phy_chain_tx_polarity_flip_physical{110.0}=0x0 +phy_chain_rx_polarity_flip_physical{110.0}=0x0 +phy_chain_tx_polarity_flip_physical{111.0}=0x1 +phy_chain_rx_polarity_flip_physical{111.0}=0x1 +phy_chain_tx_polarity_flip_physical{112.0}=0x0 +phy_chain_rx_polarity_flip_physical{112.0}=0x0 + +#wc28 P/N flip +phy_chain_tx_polarity_flip_physical{113.0}=0x1 +phy_chain_rx_polarity_flip_physical{113.0}=0x1 +phy_chain_tx_polarity_flip_physical{114.0}=0x0 +phy_chain_rx_polarity_flip_physical{114.0}=0x0 +phy_chain_tx_polarity_flip_physical{115.0}=0x1 +phy_chain_rx_polarity_flip_physical{115.0}=0x0 +phy_chain_tx_polarity_flip_physical{116.0}=0x0 +phy_chain_rx_polarity_flip_physical{116.0}=0x0 + +#wc29 P/N flip +phy_chain_tx_polarity_flip_physical{117.0}=0x1 +phy_chain_rx_polarity_flip_physical{117.0}=0x1 +phy_chain_tx_polarity_flip_physical{118.0}=0x0 +phy_chain_rx_polarity_flip_physical{118.0}=0x0 +phy_chain_tx_polarity_flip_physical{119.0}=0x1 +phy_chain_rx_polarity_flip_physical{119.0}=0x1 +phy_chain_tx_polarity_flip_physical{120.0}=0x0 +phy_chain_rx_polarity_flip_physical{120.0}=0x0 + +#wc30 P/N flip +phy_chain_tx_polarity_flip_physical{121.0}=0x1 +phy_chain_rx_polarity_flip_physical{121.0}=0x0 +phy_chain_tx_polarity_flip_physical{122.0}=0x0 +phy_chain_rx_polarity_flip_physical{122.0}=0x1 +phy_chain_tx_polarity_flip_physical{123.0}=0x1 +phy_chain_rx_polarity_flip_physical{123.0}=0x0 +phy_chain_tx_polarity_flip_physical{124.0}=0x0 +phy_chain_rx_polarity_flip_physical{124.0}=0x1 + +#wc31 P/N flip +phy_chain_tx_polarity_flip_physical{125.0}=0x1 +phy_chain_rx_polarity_flip_physical{125.0}=0x1 +phy_chain_tx_polarity_flip_physical{126.0}=0x0 +phy_chain_rx_polarity_flip_physical{126.0}=0x0 +phy_chain_tx_polarity_flip_physical{127.0}=0x1 +phy_chain_rx_polarity_flip_physical{127.0}=0x1 +phy_chain_tx_polarity_flip_physical{128.0}=0x0 +phy_chain_rx_polarity_flip_physical{128.0}=0x0 + +#MC P/N flip +phy_chain_tx_polarity_flip_physical{129.0}=0x0 +phy_chain_rx_polarity_flip_physical{129.0}=0x0 +phy_chain_tx_polarity_flip_physical{130.0}=0x0 +phy_chain_rx_polarity_flip_physical{130.0}=0x0 +phy_chain_tx_polarity_flip_physical{131.0}=0x0 +phy_chain_rx_polarity_flip_physical{131.0}=0x0 +phy_chain_tx_polarity_flip_physical{132.0}=0x0 +phy_chain_rx_polarity_flip_physical{132.0}=0x0 + + +# configuration for 100G optical module +serdes_preemphasis_1=0x164608 +serdes_preemphasis_5=0x164608 +serdes_preemphasis_9=0x164608 +serdes_preemphasis_13=0x134908 +serdes_preemphasis_17=0x134908 +serdes_preemphasis_21=0x134908 +serdes_preemphasis_25=0x124a08 +serdes_preemphasis_29=0x124a08 +serdes_preemphasis_33=0x114b08 +serdes_preemphasis_37=0x114b08 +serdes_preemphasis_41=0x0f4d08 +serdes_preemphasis_45=0x0f4d08 +serdes_preemphasis_49=0x0d4f08 +serdes_preemphasis_53=0x0d4f08 +serdes_preemphasis_57=0x0d4f08 +serdes_preemphasis_61=0x0d4f08 +serdes_preemphasis_67=0x0d4f08 +serdes_preemphasis_71=0x0d4f08 +serdes_preemphasis_75=0x0d4f08 +serdes_preemphasis_79=0x0d4f08 +serdes_preemphasis_83=0x0d4f08 +serdes_preemphasis_87=0x0f4d08 +serdes_preemphasis_91=0x0f4d08 +serdes_preemphasis_95=0x0f4d08 +serdes_preemphasis_99=0x114b08 +serdes_preemphasis_103=0x114b08 +serdes_preemphasis_107=0x114b08 +serdes_preemphasis_111=0x124a08 +serdes_preemphasis_115=0x134908 +serdes_preemphasis_119=0x134908 +serdes_preemphasis_123=0x134908 +serdes_preemphasis_127=0x164608 diff --git a/device/celestica/x86_64-cel_ds3000-r0/custom_led.bin b/device/celestica/x86_64-cel_ds3000-r0/custom_led.bin new file mode 100644 index 0000000000000000000000000000000000000000..3fb048fce6b5e36050a06ecf8b7198dce531197d GIT binary patch literal 130 zcmeycl~I{djaijhg-MCaVTv330|(>To8sm6GdN~c1u;U|O&OOY7_EdVj2u=;P1?km zaWb06iTOgJ8-pgp1to?CZI&Aj+K+BJSkK<{@VR5gsbG-47VU>O85IvEFeo!8Ix9IP dI$AX)9!#`1WK424bWCbWI-GPk`EbgIy#OM^FH8Uc literal 0 HcmV?d00001 diff --git a/device/celestica/x86_64-cel_ds3000-r0/default_sku b/device/celestica/x86_64-cel_ds3000-r0/default_sku new file mode 100644 index 000000000000..dbfb3ae8e6b9 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/default_sku @@ -0,0 +1 @@ +DS3000 t1 diff --git a/device/celestica/x86_64-cel_ds3000-r0/installer.conf b/device/celestica/x86_64-cel_ds3000-r0/installer.conf new file mode 100644 index 000000000000..6e8098123edb --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/installer.conf @@ -0,0 +1,4 @@ +CONSOLE_DEV=0 +CONSOLE_PORT=0xe060 +CONSOLE_SPEED=115200 +ONIE_PLATFORM_EXTRA_CMDLINE_LINUX="intel_iommu=off noirqdebug earlycon=uart8250,mmio,0xdf37b000" diff --git a/device/celestica/x86_64-cel_ds3000-r0/led_proc_init.soc b/device/celestica/x86_64-cel_ds3000-r0/led_proc_init.soc new file mode 100644 index 000000000000..90aa9ba607ac --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/led_proc_init.soc @@ -0,0 +1,2 @@ +m0 load 0 0x3800 /usr/share/sonic/platform/custom_led.bin +led auto on; led start diff --git a/device/celestica/x86_64-cel_ds3000-r0/pcie.yaml b/device/celestica/x86_64-cel_ds3000-r0/pcie.yaml new file mode 100644 index 000000000000..2b9612671602 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/pcie.yaml @@ -0,0 +1,165 @@ +- bus: '00' + dev: '00' + fn: '0' + id: '1980' + name: 'Host bridge: Intel Corporation Atom Processor C3000 Series System Agent (rev + 11)' +- bus: '00' + dev: '04' + fn: '0' + id: 19a1 + name: 'Host bridge: Intel Corporation Atom Processor C3000 Series Error Registers + (rev 11)' +- bus: '00' + dev: '05' + fn: '0' + id: 19a2 + name: 'Generic system peripheral [0807]: Intel Corporation Atom Processor C3000 + Series Root Complex Event Collector (rev 11)' +- bus: '00' + dev: '06' + fn: '0' + id: 19a3 + name: 'PCI bridge: Intel Corporation Atom Processor C3000 Series Integrated QAT + Root Port (rev 11)' +- bus: '00' + dev: 09 + fn: '0' + id: 19a4 + name: 'PCI bridge: Intel Corporation Atom Processor C3000 Series PCI Express Root + Port #0 (rev 11)' +- bus: '00' + dev: 0b + fn: '0' + id: 19a6 + name: 'PCI bridge: Intel Corporation Atom Processor C3000 Series PCI Express Root + Port #2 (rev 11)' +- bus: '00' + dev: 0c + fn: '0' + id: 19a7 + name: 'PCI bridge: Intel Corporation Atom Processor C3000 Series PCI Express Root + Port #3 (rev 11)' +- bus: '00' + dev: 0e + fn: '0' + id: 19a8 + name: 'PCI bridge: Intel Corporation Atom Processor C3000 Series PCI Express Root + Port #4 (rev 11)' +- bus: '00' + dev: '10' + fn: '0' + id: 19aa + name: 'PCI bridge: Intel Corporation Atom Processor C3000 Series PCI Express Root + Port #6 (rev 11)' +- bus: '00' + dev: '12' + fn: '0' + id: 19ac + name: 'System peripheral: Intel Corporation Atom Processor C3000 Series SMBus Contoller + - Host (rev 11)' +- bus: '00' + dev: '14' + fn: '0' + id: 19c2 + name: 'SATA controller: Intel Corporation Atom Processor C3000 Series SATA Controller + 1 (rev 11)' +- bus: '00' + dev: '15' + fn: '0' + id: 19d0 + name: 'USB controller: Intel Corporation Atom Processor C3000 Series USB 3.0 xHCI + Controller (rev 11)' +- bus: '00' + dev: '16' + fn: '0' + id: 19d1 + name: 'PCI bridge: Intel Corporation Atom Processor C3000 Series Integrated LAN + Root Port #0 (rev 11)' +- bus: '00' + dev: '18' + fn: '0' + id: 19d3 + name: 'Communication controller: Intel Corporation Atom Processor C3000 Series ME + HECI 1 (rev 11)' +- bus: '00' + dev: 1a + fn: '0' + id: 19d8 + name: 'Serial controller: Intel Corporation Atom Processor C3000 Series HSUART Controller + (rev 11)' +- bus: '00' + dev: 1a + fn: '1' + id: 19d8 + name: 'Serial controller: Intel Corporation Atom Processor C3000 Series HSUART Controller + (rev 11)' +- bus: '00' + dev: 1a + fn: '2' + id: 19d8 + name: 'Serial controller: Intel Corporation Atom Processor C3000 Series HSUART Controller + (rev 11)' +- bus: '00' + dev: 1c + fn: '0' + id: 19db + name: 'SD Host controller: Intel Corporation Device 19db (rev 11)' +- bus: '00' + dev: 1f + fn: '0' + id: 19dc + name: 'ISA bridge: Intel Corporation Atom Processor C3000 Series LPC or eSPI (rev + 11)' +- bus: '00' + dev: 1f + fn: '2' + id: 19de + name: 'Memory controller: Intel Corporation Atom Processor C3000 Series Power Management + Controller (rev 11)' +- bus: '00' + dev: 1f + fn: '4' + id: 19df + name: 'SMBus: Intel Corporation Atom Processor C3000 Series SMBus controller (rev + 11)' +- bus: '00' + dev: 1f + fn: '5' + id: 19e0 + name: 'Serial bus controller [0c80]: Intel Corporation Atom Processor C3000 Series + SPI Controller (rev 11)' +- bus: '01' + dev: '00' + fn: '0' + id: 19e2 + name: 'Co-processor: Intel Corporation Atom Processor C3000 Series QuickAssist Technology + (rev 11)' +- bus: '02' + dev: '00' + fn: '0' + id: b870 + name: 'Ethernet controller: Broadcom Inc. and subsidiaries Device b870 (rev 01)' +- bus: '03' + dev: '00' + fn: '0' + id: '1533' + name: 'Ethernet controller: Intel Corporation I210 Gigabit Network Connection (rev + 03)' +- bus: '06' + dev: '00' + fn: '0' + id: '7021' + name: 'Memory controller: Xilinx Corporation Device 7021' +- bus: '07' + dev: '00' + fn: '0' + id: 15c2 + name: 'Ethernet controller: Intel Corporation Ethernet Connection X553 Backplane + (rev 11)' +- bus: '07' + dev: '00' + fn: '1' + id: 15c2 + name: 'Ethernet controller: Intel Corporation Ethernet Connection X553 Backplane + (rev 11)' diff --git a/device/celestica/x86_64-cel_ds3000-r0/pddf/pd-plugin.json b/device/celestica/x86_64-cel_ds3000-r0/pddf/pd-plugin.json new file mode 100644 index 000000000000..46fd80ac4b2c --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/pddf/pd-plugin.json @@ -0,0 +1,91 @@ +{ + "PSU": + { + "psu_present": + { + "bmc": + { + "valmap": { "1":true, "0":false } + }, + "i2c": + { + "valmap": { "1":true, "0":false } + } + }, + + "psu_power_good": + { + "bmc": + { + "valmap": { "0": true, "8":false } + }, + "i2c": + { + "valmap": { "1": true, "0": false } + } + }, + + "psu_fan_dir": + { + "bmc": + { + "valmap": { "B2F":"INTAKE", "F2B":"EXHAUST" } + }, + "i2c": + { + "valmap": { "0": "INTAKE", "1":"EXHAUST" } + } + }, + + "psu_led_color": + { + "colmap": {"green":"green", "red":"amber"} + }, + + "PSU_FAN_MAX_SPEED":"18000" + }, + + "FAN": + { + "direction": + { + "bmc": + { + "valmap": { "B2F":"INTAKE", "F2B":"EXHAUST" } + }, + "i2c": + { + "valmap": { "1":"INTAKE", "0":"EXHAUST" } + } + }, + + "present": + { + "bmc": + { + "valmap": {"1":true, "0":false} + }, + "bmc": + { + "valmap": {"ok":true, "na":false} + }, + "i2c": + { + "valmap": {"0":true, "1":false} + } + }, + + "fan_master_led_color": + { + "colmap": {"green":"green", "red":"amber"} + }, + + "duty_cycle_to_pwm": "lambda dc: ((dc*255.0)/100)", + + "pwm_to_duty_cycle": "lambda pwm: ((pwm*100.0)/256)", + + "FRONT_FAN_MAX_RPM_SPEED":"24000", + "REAR_FAN_MAX_RPM_SPEED":"21000" + } + +} diff --git a/device/celestica/x86_64-cel_ds3000-r0/pddf/pddf-device-bmc.json b/device/celestica/x86_64-cel_ds3000-r0/pddf/pddf-device-bmc.json new file mode 100644 index 000000000000..73d23156c53b --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/pddf/pddf-device-bmc.json @@ -0,0 +1,2125 @@ +{ +"PLATFORM": +{ + "num_psus":2, + "num_fantrays":4, + "num_fans_pertray":2, + "num_ports":33, + "num_temps":9, + "num_components":9, + "pddf_dev_types": + { + "description":" - Below is the list of supported PDDF device types (chip names) for various components. If any component uses some other driver, we will create the client using 'echo > /new_device' method", + "CPLD": + [ + "i2c_cpld" + ], + "PSU": + [ + "psu_eeprom", + "psu_pmbus" + ], + "FAN": + [ + "fan_ctrl" + ], + "PORT_MODULE": + [ + "pddf_xcvr" + ], + "FPGAPCIE": + [ + "fpgapci" + ] + + }, + "std_perm_kos": + [ + "i2c_ismt", + "i2c-i801" + ], + + "std_kos": + [ + "lpc_ich", + "i2c_dev", + "ipmi_devintf", + "ipmi_si", + "i2c_mux_pca954x", + "optoe", + "mc24lc64t", + "baseboard_cpld" + ], + + "pddf_kos": + [ + "pddf_client_module", + "pddf_cpld_module", + "pddf_cpld_driver", + "pddf_mux_module", + "pddf_led_module", + "pddf_fpgai2c_module", + "pddf_fpgai2c_driver", + "pddf_xcvr_module", + "pddf_xcvr_driver_module", + "pddf_fpgapci_driver", + "pddf_fpgapci_module" + ], + + "custom_kos": + [ + "pddf_custom_fpga_algo" + ] + }, + + "COMPONENT1": + { + "comp_attr":{ "name": "BIOS", "type": "bios", "description": "Basic Input/Output System"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "echo `dmidecode -s bios-version`-`dmidecode -s bios-release-date`" }, + { "attr_name":"update", "cmd": "afulnx_64 {} /p /b /n /me /x /k" } + ] + }, + "COMPONENT2": + { + "comp_attr":{ "name": "BMC", "type": "bmc", "description":"Baseboard Management Controller "}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "r=(`ipmitool raw 0x6 0x1 | cut -d ' ' -f 4,5,16,15,14`) && echo ${r[0]}.${r[1]}.${r[4]}.${r[3]}${r[2]}" } + ] + }, + "COMPONENT3": + { + "comp_attr":{ "name": "BaseBoard_CPLD", "type": "cpld", "description": "Base Board CPLD"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "r=$(cat /sys/devices/platform/baseboard/version) && printf '%d.%d' $(($r>>4)) $(($r&0xf))" }, + { "attr_name":"update", "cmd": "ispvm {}" } + ] + }, + "COMPONENT4": + { + "comp_attr":{ "name": "SwitchBoard_CPLD1", "type": "cpld", "description":"Switch Board CPLD"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "r=$(i2cget -y -f 102 0x30 0) && printf '%d.%d' $(($r>>4)) $(($r&0xf))" } + ] + }, + "COMPONENT5": + { + "comp_attr":{ "name": "SwitchBoard_CPLD2", "type": "cpld", "description":"Switch Board CPLD"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "r=$(i2cget -y -f 102 0x31 0) && printf '%d.%d' $(($r>>4)) $(($r&0xf))" } + ] + }, + "COMPONENT6": + { + "comp_attr":{ "name": "COMeBoard_CPLD", "type": "cpld", "description": "COMe Board CPLD"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "r=$(cat /sys/devices/platform/baseboard/come_cpld_version) && printf '%d.%d' $(($r>>4)) $(($r&0xf))" } + ] + }, + "COMPONENT7": + { + "comp_attr":{ "name": "FPGA", "type": "fpga", "description": "Baseboard FPGA"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "r=$(cat /sys/devices/platform/fpga_sysfs/version) && printf '%d.%d' $(($r>>16)) $(($r&0xffff))" }, + { "attr_name":"update", "cmd": "fpga_prog /sys/bus/pci/devices/0000:06:00.0/resource0 {}" } + ] + }, + "COMPONENT8": + { + "comp_attr":{ "name": "PCIe", "type": "pcie", "description":"ASIC PCIe Firmware"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "bcmcmd 'pciephy fw version' | grep 'PCIe FW version' | cut -d ' ' -f 4" } + ] + }, + "COMPONENT9": + { + "comp_attr":{ "name": "SSD", "type": "ssd", "description":"SSD firmware version"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "ssdutil -v | grep 'Firmware' | awk '{ print $3 }'" } + ] + }, + + "SYSTEM": + { + "dev_info": {"device_type":"CPU", "device_name":"ROOT_COMPLEX", "device_parent":null}, + "i2c": + { + "CONTROLLERS": + [ + { "dev_name":"i2c-0", "dev":"SMBUS0" }, + { "dev_name":"i2c-1", "dev":"SMBUS1" }, + { "dev_name":"pcie-0", "dev":"PCIE0" } + ] + } + }, + + "SMBUS0": + { + "dev_info": {"device_type": "SMBUS", "device_name": "SMBUS0", "device_parent": "SYSTEM"}, + "i2c": + { + "topo_info": {"dev_addr": "0x0"}, + "DEVICES": + [ + {"dev": "EEPROM1"} + ] + } + }, + + "EEPROM1": + { + "dev_info": {"device_type": "EEPROM", "device_name": "EEPROM1", "device_parent": "SMBUS0"}, + "i2c": + { + "topo_info": {"parent_bus": "0x0", "dev_addr": "0x56", "dev_type": "24lc64t"}, + "dev_attr": {"access_mode": "BLOCK"}, + "attr_list": [ + {"attr_name": "eeprom"} + ] + } + }, + + "SMBUS1": + { + "dev_info": {"device_type": "SMBUS", "device_name": "SMBUS1", "device_parent": "SYSTEM"}, + "i2c": + { + "topo_info": {"dev_addr": "0x1"}, + "DEVICES": + [ + {"dev": "COME_CPLD"} + ] + } + }, + + "COME_CPLD": + { + "dev_info": {"device_type": "CPLD", "device_name": "COME_CPLD", "device_parent": "SMBUS1"}, + "i2c": + { + "topo_info": {"parent_bus": "0x1", "dev_addr": "0x0d", "dev_type": "i2c_cpld"}, + "dev_attr": {} + } + }, + + "PCIE0": + { + "dev_info": {"device_type": "PCIE", "device_name": "PCIE0", "device_parent": "SYSTEM"}, + "i2c": + { + "DEVICES": + [ + {"dev": "FPGAPCIE0"} + ] + } + }, + + "FPGAPCIE0": + { + "dev_info": {"device_type": "FPGAPCIE", "device_name": "FPGAPCIE0", "device_parent": "PCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x0"}, + "dev_attr": { "vendor_id":"0x10EE", "device_id": "0x7021", "virt_bus": "0x64", "data_base_offset":"0x0", "data_size":"0x25000", "i2c_ch_base_offset":"0x10000", "i2c_ch_size":"0x1000", "virt_i2c_ch":"0xc"}, + "channel": + [ + { "chn":"3", "dev":"CPLD_S1" }, + { "chn":"3", "dev":"CPLD_S2" }, + { "chn":"2", "dev":"MUX1" }, + { "chn":"2", "dev":"MUX2" }, + { "chn":"2", "dev":"MUX3" }, + { "chn":"2", "dev":"MUX4" }, + { "chn":"1", "dev":"MUX5" } + ] + } + }, + + "CPLD_S1": + { + "dev_info": { "device_type":"CPLD", "device_name":"CPLD_S1", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x66", "dev_addr":"0x30", "dev_type":"i2c_cpld"}, + "dev_attr":{} + } + }, + + "CPLD_S2": + { + "dev_info": { "device_type":"CPLD", "device_name":"CPLD_S2", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x66", "dev_addr":"0x31", "dev_type":"i2c_cpld"}, + "dev_attr":{} + } + }, + + "MUX1": + { + "dev_info": { "device_type":"MUX", "device_name":"MUX1", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x65", "dev_addr":"0x72", "dev_type":"pca9548"}, + "dev_attr": { "virt_bus":"0x2", "idle_state":"-2" }, + "channel": + [ + { "chn":"0", "dev":"PORT1" }, + { "chn":"1", "dev":"PORT2" }, + { "chn":"2", "dev":"PORT3" }, + { "chn":"3", "dev":"PORT4" }, + { "chn":"4", "dev":"PORT5" }, + { "chn":"5", "dev":"PORT6" }, + { "chn":"6", "dev":"PORT7" }, + { "chn":"7", "dev":"PORT8" } + ] + } + }, + + "MUX2": + { + "dev_info": { "device_type":"MUX", "device_name":"MUX2", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x65", "dev_addr":"0x73", "dev_type":"pca9548"}, + "dev_attr": { "virt_bus":"0xa", "idle_state":"-2" }, + "channel": + [ + { "chn":"0", "dev":"PORT9" }, + { "chn":"1", "dev":"PORT10" }, + { "chn":"2", "dev":"PORT11" }, + { "chn":"3", "dev":"PORT12" }, + { "chn":"4", "dev":"PORT13" }, + { "chn":"5", "dev":"PORT14" }, + { "chn":"6", "dev":"PORT15" }, + { "chn":"7", "dev":"PORT16" } + ] + } + }, + + "MUX3": + { + "dev_info": { "device_type":"MUX", "device_name":"MUX3", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x65", "dev_addr":"0x74", "dev_type":"pca9548"}, + "dev_attr": { "virt_bus":"0x12", "idle_state":"-2" }, + "channel": + [ + { "chn":"0", "dev":"PORT17" }, + { "chn":"1", "dev":"PORT18" }, + { "chn":"2", "dev":"PORT19" }, + { "chn":"3", "dev":"PORT20" }, + { "chn":"4", "dev":"PORT21" }, + { "chn":"5", "dev":"PORT22" }, + { "chn":"6", "dev":"PORT23" }, + { "chn":"7", "dev":"PORT24" } + ] + } + }, + + "MUX4": + { + "dev_info": { "device_type":"MUX", "device_name":"MUX4", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x65", "dev_addr":"0x75", "dev_type":"pca9548"}, + "dev_attr": { "virt_bus":"0x1a", "idle_state":"-2" }, + "channel": + [ + { "chn":"0", "dev":"PORT25" }, + { "chn":"1", "dev":"PORT26" }, + { "chn":"2", "dev":"PORT27" }, + { "chn":"3", "dev":"PORT28" }, + { "chn":"4", "dev":"PORT29" }, + { "chn":"5", "dev":"PORT30" }, + { "chn":"6", "dev":"PORT31" }, + { "chn":"7", "dev":"PORT32" } + ] + } + }, + "MUX5": + { + "dev_info": { "device_type":"MUX", "device_name":"MUX5", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x64", "dev_addr":"0x72", "dev_type":"pca9548"}, + "dev_attr": { "virt_bus":"0x22"}, + "channel": + [ + { "chn":"0", "dev":"PORT33" } + ] + } + }, + + "PORT1": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT1", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"1"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT1-EEPROM" }, + { "itf":"control", "dev":"PORT1-CTRL" } + + ] + } + }, + "PORT1-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT1-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT1"}, + "i2c": + { + "topo_info": { "parent_bus":"0x2", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + "PORT1-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT1-CTRL", "device_parent":"MUX1", "virt_parent":"PORT1"}, + "i2c": + { + "topo_info": { "parent_bus":"0x2", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + "PORT2": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT2", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"2"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT2-EEPROM" }, + { "itf":"control", "dev":"PORT2-CTRL" } + ] + } + }, + + "PORT2-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT2-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT2"}, + "i2c": + { + "topo_info": { "parent_bus":"0x3", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT2-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT2-CTRL", "device_parent":"MUX1", "virt_parent":"PORT2"}, + "i2c": + { + "topo_info": { "parent_bus":"0x3", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x10", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x10", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x10", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x10", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT3": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT3", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"3"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT3-EEPROM" }, + { "itf":"control", "dev":"PORT3-CTRL" } + ] + } + }, + "PORT3-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT3-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT3"}, + "i2c": + { + "topo_info": { "parent_bus":"0x4", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT3-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT3-CTRL", "device_parent":"MUX1", "virt_parent":"PORT3"}, + "i2c": + { + "topo_info": { "parent_bus":"0x4", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x20", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x20", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x20", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x20", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT4": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT4", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"4"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT4-EEPROM" }, + { "itf":"control", "dev":"PORT4-CTRL" } + ] + } + }, + "PORT4-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT4-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT4"}, + "i2c": + { + "topo_info": { "parent_bus":"0x5", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT4-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT4-CTRL", "device_parent":"MUX1", "virt_parent":"PORT4"}, + "i2c": + { + "topo_info": { "parent_bus":"0x5", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x30", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x30", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x30", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x30", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT5": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT5", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"5"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT5-EEPROM" }, + { "itf":"control", "dev":"PORT5-CTRL" } + ] + } + }, + + "PORT5-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT5-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT5"}, + "i2c": + { + "topo_info": { "parent_bus":"0x6", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT5-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT5-CTRL", "device_parent":"MUX1", "virt_parent":"PORT5"}, + "i2c": + { + "topo_info": { "parent_bus":"0x6", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x40", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x40", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x40", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x40", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT6": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT6", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"6"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT6-EEPROM" }, + { "itf":"control", "dev":"PORT6-CTRL" } + ] + } + }, + "PORT6-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT6-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT6"}, + "i2c": + { + "topo_info": { "parent_bus":"0x7", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT6-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT6-CTRL", "device_parent":"MUX1", "virt_parent":"PORT6"}, + "i2c": + { + "topo_info": { "parent_bus":"0x7", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x50", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x50", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x50", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x50", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT7": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT7", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"7"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT7-EEPROM" }, + { "itf":"control", "dev":"PORT7-CTRL" } + ] + } + }, + "PORT7-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT7-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT7"}, + "i2c": + { + "topo_info": { "parent_bus":"0x8", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT7-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT7-CTRL", "device_parent":"MUX1", "virt_parent":"PORT7"}, + "i2c": + { + "topo_info": { "parent_bus":"0x8", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x60", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x60", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x60", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x60", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT8": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT8", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"8"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT8-EEPROM" }, + { "itf":"control", "dev":"PORT8-CTRL" } + ] + } + }, + "PORT8-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT8-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT8"}, + "i2c": + { + "topo_info": { "parent_bus":"0x9", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT8-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT8-CTRL", "device_parent":"MUX1", "virt_parent":"PORT8"}, + "i2c": + { + "topo_info": { "parent_bus":"0x9", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x70", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x70", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x70", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x70", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT9": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT9", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"9"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT9-EEPROM" }, + { "itf":"control", "dev":"PORT9-CTRL" } + ] + } + }, + "PORT9-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT9-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT9"}, + "i2c": + { + "topo_info": { "parent_bus":"0xa", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT9-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT9-CTRL", "device_parent":"MUX2", "virt_parent":"PORT9"}, + "i2c": + { + "topo_info": { "parent_bus":"0xa", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x80", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x80", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x80", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x80", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT10": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT10", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"10"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT10-EEPROM" }, + { "itf":"control", "dev":"PORT10-CTRL" } + ] + } + }, + "PORT10-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT10-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT10"}, + "i2c": + { + "topo_info": { "parent_bus":"0xb", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT10-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT10-CTRL", "device_parent":"MUX2", "virt_parent":"PORT10"}, + "i2c": + { + "topo_info": { "parent_bus":"0xb", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x90", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x90", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x90", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x90", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT11": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT11", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"11"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT11-EEPROM" }, + { "itf":"control", "dev":"PORT11-CTRL" } + ] + } + }, + "PORT11-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT11-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT11"}, + "i2c": + { + "topo_info": { "parent_bus":"0xc", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT11-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT11-CTRL", "device_parent":"MUX2", "virt_parent":"PORT11"}, + "i2c": + { + "topo_info": { "parent_bus":"0xc", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xa0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xa0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xa0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xa0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT12": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT12", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"12"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT12-EEPROM" }, + { "itf":"control", "dev":"PORT12-CTRL" } + ] + } + }, + "PORT12-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT12-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT12"}, + "i2c": + { + "topo_info": { "parent_bus":"0xd", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT12-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT12-CTRL", "device_parent":"MUX2", "virt_parent":"PORT12"}, + "i2c": + { + "topo_info": { "parent_bus":"0xd", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xb0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xb0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xb0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xb0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT13": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT13", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"13"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT13-EEPROM" }, + { "itf":"control", "dev":"PORT13-CTRL" } + ] + } + }, + "PORT13-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT13-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT13"}, + "i2c": + { + "topo_info": { "parent_bus":"0xe", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT13-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT13-CTRL", "device_parent":"MUX2", "virt_parent":"PORT13"}, + "i2c": + { + "topo_info": { "parent_bus":"0xe", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xc0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xc0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xc0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xc0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT14": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT14", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"14"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT14-EEPROM" }, + { "itf":"control", "dev":"PORT14-CTRL" } + ] + } + }, + "PORT14-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT14-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT14"}, + "i2c": + { + "topo_info": { "parent_bus":"0xF", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT14-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT14-CTRL", "device_parent":"MUX2", "virt_parent":"PORT14"}, + "i2c": + { + "topo_info": { "parent_bus":"0xf", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xd0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xd0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xd0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xd0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT15": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT15", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"15"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT15-EEPROM" }, + { "itf":"control", "dev":"PORT15-CTRL" } + ] + } + }, + "PORT15-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT15-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT15"}, + "i2c": + { + "topo_info": { "parent_bus":"0x10", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT15-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT15-CTRL", "device_parent":"MUX2", "virt_parent":"PORT15"}, + "i2c": + { + "topo_info": { "parent_bus":"0x10", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xe0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xe0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xe0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xe0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT16": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT16", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"16"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT16-EEPROM" }, + { "itf":"control", "dev":"PORT16-CTRL" } + ] + } + }, + "PORT16-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT16-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT16"}, + "i2c": + { + "topo_info": { "parent_bus":"0x11", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT16-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT16-CTRL", "device_parent":"MUX2", "virt_parent":"PORT16"}, + "i2c": + { + "topo_info": { "parent_bus":"0x11", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xf0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xf0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xf0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xf0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT17": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT17", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"17"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT17-EEPROM" }, + { "itf":"control", "dev":"PORT17-CTRL" } + ] + } + }, + "PORT17-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT17-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT17"}, + "i2c": + { + "topo_info": { "parent_bus":"0x12", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT17-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT17-CTRL", "device_parent":"MUX3", "virt_parent":"PORT17"}, + "i2c": + { + "topo_info": { "parent_bus":"0x12", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x100", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x100", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x100", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x100", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT18": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT18", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"18"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT18-EEPROM" }, + { "itf":"control", "dev":"PORT18-CTRL" } + ] + } + }, + "PORT18-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT18-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT18"}, + "i2c": + { + "topo_info": { "parent_bus":"0x13", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT18-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT18-CTRL", "device_parent":"MUX3", "virt_parent":"PORT18"}, + "i2c": + { + "topo_info": { "parent_bus":"0x13", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x110", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x110", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x110", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x110", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT19": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT19", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"19"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT19-EEPROM" }, + { "itf":"control", "dev":"PORT19-CTRL" } + ] + } + }, + "PORT19-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT19-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT19"}, + "i2c": + { + "topo_info": { "parent_bus":"0x14", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT19-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT19-CTRL", "device_parent":"MUX3", "virt_parent":"PORT19"}, + "i2c": + { + "topo_info": { "parent_bus":"0x14", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x120", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x120", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x120", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x120", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT20": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT20", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"20"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT20-EEPROM" }, + { "itf":"control", "dev":"PORT20-CTRL" } + ] + } + }, + "PORT20-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT20-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT20"}, + "i2c": + { + "topo_info": { "parent_bus":"0x15", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT20-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT20-CTRL", "device_parent":"MUX3", "virt_parent":"PORT20"}, + "i2c": + { + "topo_info": { "parent_bus":"0x15", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x130", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x130", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x130", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x130", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT21": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT21", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"21"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT21-EEPROM" }, + { "itf":"control", "dev":"PORT21-CTRL" } + ] + } + }, + "PORT21-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT21-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT21"}, + "i2c": + { + "topo_info": { "parent_bus":"0x16", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT21-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT21-CTRL", "device_parent":"MUX3", "virt_parent":"PORT21"}, + "i2c": + { + "topo_info": { "parent_bus":"0x16", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x140", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x140", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x140", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x140", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT22": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT22", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"22"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT22-EEPROM" }, + { "itf":"control", "dev":"PORT22-CTRL" } + ] + } + }, + "PORT22-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT22-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT22"}, + "i2c": + { + "topo_info": { "parent_bus":"0x17", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT22-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT22-CTRL", "device_parent":"MUX3", "virt_parent":"PORT22"}, + "i2c": + { + "topo_info": { "parent_bus":"0x17", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x150", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x150", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x150", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x150", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT23": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT23", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"23"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT23-EEPROM" }, + { "itf":"control", "dev":"PORT23-CTRL" } + ] + } + }, + "PORT23-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT23-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT23"}, + "i2c": + { + "topo_info": { "parent_bus":"0x18", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT23-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT23-CTRL", "device_parent":"MUX3", "virt_parent":"PORT23"}, + "i2c": + { + "topo_info": { "parent_bus":"0x18", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x160", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x160", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x160", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x160", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT24": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT24", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"24"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT24-EEPROM" }, + { "itf":"control", "dev":"PORT24-CTRL" } + ] + } + }, + "PORT24-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT24-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT24"}, + "i2c": + { + "topo_info": { "parent_bus":"0x19", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT24-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT24-CTRL", "device_parent":"MUX3", "virt_parent":"PORT24"}, + "i2c": + { + "topo_info": { "parent_bus":"0x19", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x170", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x170", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x170", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x170", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT25": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT25", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"25"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT25-EEPROM" }, + { "itf":"control", "dev":"PORT25-CTRL" } + ] + } + }, + "PORT25-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT25-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT25"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1a", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT25-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT25-CTRL", "device_parent":"MUX4", "virt_parent":"PORT25"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1a", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x180", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x180", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x180", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x180", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT26": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT26", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"26"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT26-EEPROM" }, + { "itf":"control", "dev":"PORT26-CTRL" } + ] + } + }, + "PORT26-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT26-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT26"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1b", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT26-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT26-CTRL", "device_parent":"MUX4", "virt_parent":"PORT26"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1b", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x190", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x190", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x190", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x190", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT27": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT27", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"27"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT27-EEPROM" }, + { "itf":"control", "dev":"PORT27-CTRL" } + ] + } + }, + "PORT27-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT27-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT27"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1c", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT27-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT27-CTRL", "device_parent":"MUX4", "virt_parent":"PORT27"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1c", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1a0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1a0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1a0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1a0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT28": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT28", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"28"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT28-EEPROM" }, + { "itf":"control", "dev":"PORT28-CTRL" } + ] + } + }, + "PORT28-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT28-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT28"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1d", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT28-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT28-CTRL", "device_parent":"MUX4", "virt_parent":"PORT28"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1d", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1b0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1b0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1b0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1b0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT29": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT29", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"29"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT29-EEPROM" }, + { "itf":"control", "dev":"PORT29-CTRL" } + ] + } + }, + "PORT29-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT29-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT29"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1e", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT29-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT29-CTRL", "device_parent":"MUX4", "virt_parent":"PORT29"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1e", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1c0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1c0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1c0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1c0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT30": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT30", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"30"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT30-EEPROM" }, + { "itf":"control", "dev":"PORT30-CTRL" } + ] + } + }, + "PORT30-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT30-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT30"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1f", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT30-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT30-CTRL", "device_parent":"MUX4", "virt_parent":"PORT30"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1f", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1d0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1d0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1d0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1d0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT31": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT31", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"31"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT31-EEPROM" }, + { "itf":"control", "dev":"PORT31-CTRL" } + ] + } + }, + "PORT31-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT31-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT31"}, + "i2c": + { + "topo_info": { "parent_bus":"0x20", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT31-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT31-CTRL", "device_parent":"MUX4", "virt_parent":"PORT31"}, + "i2c": + { + "topo_info": { "parent_bus":"0x20", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1e0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1e0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1e0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1e0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT32": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT32", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"32"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT32-EEPROM" }, + { "itf":"control", "dev":"PORT32-CTRL" } + ] + } + }, + "PORT32-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT32-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT32"}, + "i2c": + { + "topo_info": { "parent_bus":"0x21", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT32-CTRL": + { + "dev_info": { "device_type":"pci", "device_name":"PORT32-CTRL", "device_parent":"MUX4", "virt_parent":"PORT32"}, + "i2c": + { + "topo_info": { "parent_bus":"0x21", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1f0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1f0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1f0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1f0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT33": + { + "dev_info": { "device_type":"SFP+", "device_name":"PORT33", "device_parent":"MUX5"}, + "dev_attr": { "dev_idx":"33"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT33-EEPROM" }, + { "itf":"control", "dev":"PORT33-CTRL" } + ] + } + }, + "PORT33-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT33-EEPROM", "device_parent":"MUX5", "virt_parent":"PORT33"}, + "i2c": + { + "topo_info": { "parent_bus":"0x22", "dev_addr":"0x50", "dev_type":"optoe2"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT33-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT33-CTRL", "device_parent":"MUX5", "virt_parent":"PORT33"}, + "i2c": + { + "topo_info": { "parent_bus":"0x22", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x200", "attr_mask":"0x0", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_txfault", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x200", "attr_mask":"0x2", "attr_cmpval":"0x4", "attr_len":"1"}, + { "attr_name":"xcvr_txdisable", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x200", "attr_mask":"0x0", "attr_cmpval":"0x1", "attr_len":"1"}, + { "attr_name":"xcvr_rxlos", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x200", "attr_mask":"0x1", "attr_cmpval":"0x2", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x200", "attr_mask":"0x0", "attr_cmpval":"0x1", "attr_len":"1"} + ] + } + }, + + "PSU1": + { + "dev_info": { "device_type":"PSU"}, + "dev_attr": { "dev_idx":"1", "num_psu_fans": "1"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"psu_power_good", "bmc_cmd":"ipmitool raw 0x04 0x2d 0x0f | awk '{print substr($0,9,1)}'", "raw": "1", "type":"mask", "mask":"0x8"}, + { "attr_name":"psu_present", "bmc_cmd":"ipmitool raw 0x04 0x2d 0x0f | awk '{print substr($0,9,1)}'", "raw": "1", "type":"mask", "mask":"0x1"}, + { "attr_name":"psu_model_name", "bmc_cmd":"ipmitool fru print 3", "raw": "0", "field_name":"Product Name", "separator":": ","field_pos":"2"}, + { "attr_name":"psu_serial_num", "bmc_cmd":"ipmitool fru print 3", "raw": "0", "field_name":"Product Serial", "separator":": ","field_pos":"2"}, + { "attr_name":"psu_mfr_id", "bmc_cmd":"ipmitool fru print 3", "raw": "0", "field_name":"Product Manufacturer", "separator":":", "field_pos":"2"}, + { "attr_name":"psu_p_out", "bmc_cmd":"ipmitool sdr", "raw": "0", "field_name":"PSU1_POut", "field_pos":"3", "mult":"1000000"}, + { "attr_name":"psu_v_out", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "PSU1_VOut", "field_pos":"3", "mult":"1000"}, + { "attr_name":"psu_i_out", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "PSU1_COut", "field_pos":"3", "mult":"1000"}, + { "attr_name":"psu_p_in", "bmc_cmd":"ipmitool sdr", "raw": "0", "field_name": "PSU1_PIn", "field_pos":"3", "mult":"1000000"}, + { "attr_name":"psu_v_in", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "PSU1_VIn", "field_pos":"3", "mult":"1000"}, + { "attr_name":"psu_i_in", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "PSU1_CIn", "field_pos":"3", "mult":"1000"}, + { "attr_name":"psu_fan1_speed_rpm", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "PSU1_Fan", "field_pos":"3", "mult":"1"}, + { "attr_name":"psu_temp1_input", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "PSU1_Temp1", "field_pos":"3", "mult":"1000"}, + { "attr_name":"psu_temp1_high_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"PSU1_Temp1", "field_pos":"18", "mult":"1000"} + ] + } + } + }, + + "PSU2": + { + "dev_info": { "device_type":"PSU"}, + "dev_attr": { "dev_idx":"2", "num_psu_fans": "1"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"psu_power_good", "bmc_cmd":"ipmitool raw 0x04 0x2d 0x10 | awk '{print substr($0,9,1)}'", "raw": "1", "type":"mask", "mask":"0x8"}, + { "attr_name":"psu_present", "bmc_cmd":"ipmitool raw 0x04 0x2d 0x10 | awk '{print substr($0,9,1)}'", "raw": "1", "type":"mask", "mask":"0x1"}, + { "attr_name":"psu_model_name", "bmc_cmd":"ipmitool fru print 4", "raw": "0", "field_name":"Product Name", "separator":": ","field_pos":"2"}, + { "attr_name":"psu_serial_num", "bmc_cmd":"ipmitool fru print 4", "raw": "0", "field_name":"Product Serial", "separator":": ","field_pos":"2"}, + { "attr_name":"psu_mfr_id", "bmc_cmd":"ipmitool fru print 4", "raw": "0", "field_name":"Product Manufacturer", "separator":":", "field_pos":"2"}, + { "attr_name":"psu_p_out", "bmc_cmd":"ipmitool sdr", "raw": "0", "field_name":"PSU2_POut", "field_pos":"3", "mult":"1000000"}, + { "attr_name":"psu_v_out", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "PSU2_VOut", "field_pos":"3", "mult":"1000"}, + { "attr_name":"psu_i_out", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "PSU2_COut", "field_pos":"3", "mult":"1000"}, + { "attr_name":"psu_p_in", "bmc_cmd":"ipmitool sdr", "raw": "0", "field_name": "PSU2_PIn", "field_pos":"3", "mult":"1000000"}, + { "attr_name":"psu_v_in", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "PSU2_VIn", "field_pos":"3", "mult":"1000"}, + { "attr_name":"psu_i_in", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "PSU2_CIn", "field_pos":"3", "mult":"1000"}, + { "attr_name":"psu_fan1_speed_rpm", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "PSU2_Fan", "field_pos":"3", "mult":"1"}, + { "attr_name":"psu_temp1_input", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "PSU2_Temp1", "field_pos":"3", "mult":"1000"}, + { "attr_name":"psu_temp1_high_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"PSU2_Temp1", "field_pos":"18", "mult":"1000"} + ] + } + } + }, + + "FAN-CTRL": + { + "dev_info": { "device_type":"FAN"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"fan1_present", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "Fan1_Status", "field_pos":"5"}, + { "attr_name":"fan2_present", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "Fan1_Status", "field_pos":"5"}, + { "attr_name":"fan3_present", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "Fan2_Status", "field_pos":"5"}, + { "attr_name":"fan4_present", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "Fan2_Status", "field_pos":"5"}, + { "attr_name":"fan5_present", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "Fan3_Status", "field_pos":"5"}, + { "attr_name":"fan6_present", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "Fan3_Status", "field_pos":"5"}, + { "attr_name":"fan7_present", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "Fan4_Status", "field_pos":"5"}, + { "attr_name":"fan8_present", "bmc_cmd":"ipmitool sdr", "raw":"0", "field_name" : "Fan4_Status", "field_pos":"5"}, + { "attr_name":"fan1_direction", "bmc_cmd":"ipmitool fru print 5 | grep -e B2F -e F2B", "raw": "0", "field_name":"Board Extra", "separator":": ", "field_pos":"2"}, + { "attr_name":"fan2_direction", "bmc_cmd":"ipmitool fru print 5 | grep -e B2F -e F2B", "raw": "0", "field_name":"Board Extra", "separator":": ", "field_pos":"2"}, + { "attr_name":"fan3_direction", "bmc_cmd":"ipmitool fru print 6 | grep -e B2F -e F2B", "raw": "0", "field_name":"Board Extra", "separator":": ", "field_pos":"2"}, + { "attr_name":"fan4_direction", "bmc_cmd":"ipmitool fru print 6 | grep -e B2F -e F2B", "raw": "0", "field_name":"Board Extra", "separator":": ", "field_pos":"2"}, + { "attr_name":"fan5_direction", "bmc_cmd":"ipmitool fru print 7 | grep -e B2F -e F2B", "raw": "0", "field_name":"Board Extra", "separator":": ", "field_pos":"2"}, + { "attr_name":"fan6_direction", "bmc_cmd":"ipmitool fru print 7 | grep -e B2F -e F2B", "raw": "0", "field_name":"Board Extra", "separator":": ", "field_pos":"2"}, + { "attr_name":"fan7_direction", "bmc_cmd":"ipmitool fru print 8 | grep -e B2F -e F2B", "raw": "0", "field_name":"Board Extra", "separator":": ", "field_pos":"2"}, + { "attr_name":"fan8_direction", "bmc_cmd":"ipmitool fru print 8 | grep -e B2F -e F2B", "raw": "0", "field_name":"Board Extra", "separator":": ", "field_pos":"2"}, + { "attr_name":"fan1_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan1_Front", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan2_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan1_Rear", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan3_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan2_Front", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan4_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan2_Rear", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan5_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan3_Front", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan6_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan3_Rear", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan7_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan4_Front", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan8_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan4_Rear", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan1_pwm", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan1_Front", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan2_pwm", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan1_Rear", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan3_pwm", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan2_Front", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan4_pwm", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan2_Rear", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan5_pwm", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan3_Front", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan6_pwm", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan3_Rear", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan7_pwm", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan4_Front", "field_pos":"3", "mult":"1"}, + { "attr_name":"fan8_pwm", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Fan4_Rear", "field_pos":"3", "mult":"1"} + ] + } + } + }, + + "TEMP1": + { + "dev_info": { "device_type":"TEMP_SENSOR"}, + "dev_attr": { "display_name":"CPU_Temp"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"temp1_high_crit_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"CPU_Temp", "field_pos":"20"}, + { "attr_name":"temp1_high_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"CPU_Temp", "field_pos":"18"}, + { "attr_name":"temp1_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "CPU_Temp", "field_pos":"3"}, + { "attr_name":"temp1_low_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "CPU_Temp", "field_pos":"14"} + + ] + } + } + }, + "TEMP2": + { + "dev_info": { "device_type":"TEMP_SENSOR"}, + "dev_attr": { "display_name":"Base_Temp_U5"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"temp1_high_crit_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"Base_Temp_U5", "field_pos":"20"}, + { "attr_name":"temp1_high_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"Base_Temp_U5", "field_pos":"18"}, + { "attr_name":"temp1_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Base_Temp_U5", "field_pos":"3"}, + { "attr_name":"temp1_low_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Base_Temp_U5", "field_pos":"14"} + + ] + } + } + }, + "TEMP3": + { + "dev_info": { "device_type":"TEMP_SENSOR"}, + "dev_attr": { "display_name":"Base_Temp_U56"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"temp1_high_crit_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"Base_Temp_U56", "field_pos":"20"}, + { "attr_name":"temp1_high_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"Base_Temp_U56", "field_pos":"18"}, + { "attr_name":"temp1_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Base_Temp_U56", "field_pos":"3"}, + { "attr_name":"temp1_low_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Base_Temp_U56", "field_pos":"14"} + + ] + } + } + }, + "TEMP4": + { + "dev_info": { "device_type":"TEMP_SENSOR"}, + "dev_attr": { "display_name":"Switch_Temp_U17"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"temp1_high_crit_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"Switch_Temp_U17", "field_pos":"20"}, + { "attr_name":"temp1_high_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"Switch_Temp_U17", "field_pos":"18"}, + { "attr_name":"temp1_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Switch_Temp_U17", "field_pos":"3"}, + { "attr_name":"temp1_low_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Switch_Temp_U17", "field_pos":"14"} + + ] + } + } + }, + "TEMP5": + { + "dev_info": { "device_type":"TEMP_SENSOR"}, + "dev_attr": { "display_name":"Switch_Temp_U18"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"temp1_high_crit_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"Switch_Temp_U18", "field_pos":"20"}, + { "attr_name":"temp1_high_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"Switch_Temp_U18", "field_pos":"18"}, + { "attr_name":"temp1_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Switch_Temp_U18", "field_pos":"3"}, + { "attr_name":"temp1_low_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Switch_Temp_U18", "field_pos":"14"} + + ] + } + } + }, + "TEMP6": + { + "dev_info": { "device_type":"TEMP_SENSOR"}, + "dev_attr": { "display_name":"Switch_Temp_U28"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"temp1_high_crit_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"Switch_Temp_U28", "field_pos":"20"}, + { "attr_name":"temp1_high_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"Switch_Temp_U28", "field_pos":"18"}, + { "attr_name":"temp1_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Switch_Temp_U28", "field_pos":"3"}, + { "attr_name":"temp1_low_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Switch_Temp_U28", "field_pos":"14"} + + ] + } + } + }, + "TEMP7": + { + "dev_info": { "device_type":"TEMP_SENSOR"}, + "dev_attr": { "display_name":"Switch_Temp_U29"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"temp1_high_crit_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"Switch_Temp_U29", "field_pos":"20"}, + { "attr_name":"temp1_high_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"Switch_Temp_U29", "field_pos":"18"}, + { "attr_name":"temp1_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Switch_Temp_U29", "field_pos":"3"}, + { "attr_name":"temp1_low_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "Switch_Temp_U29", "field_pos":"14"} + + ] + } + } + }, + "TEMP8": + { + "dev_info": { "device_type":"TEMP_SENSOR"}, + "dev_attr": { "display_name":"VDD_CORE_Temp"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"temp1_high_crit_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"VDD_CORE_Temp", "field_pos":"20"}, + { "attr_name":"temp1_high_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"VDD_CORE_Temp", "field_pos":"18"}, + { "attr_name":"temp1_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "VDD_CORE_Temp", "field_pos":"3"}, + { "attr_name":"temp1_low_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "VDD_CORE_Temp", "field_pos":"14"} + + ] + } + } + }, + "TEMP9": + { + "dev_info": { "device_type":"TEMP_SENSOR"}, + "dev_attr": { "display_name":"VDD_ANLG_Temp"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"temp1_high_crit_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"VDD_ANLG_Temp", "field_pos":"20"}, + { "attr_name":"temp1_high_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name":"VDD_ANLG_Temp", "field_pos":"18"}, + { "attr_name":"temp1_input", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "VDD_ANLG_Temp", "field_pos":"3"}, + { "attr_name":"temp1_low_threshold", "bmc_cmd":"ipmitool sensor", "raw":"0", "field_name" : "VDD_ANLG_Temp", "field_pos":"14"} + + ] + } + } + }, + + "SYS_LED": + { + "dev_info": { "device_type":"LED", "device_name":"SYS_LED"}, + "dev_attr": { "index":"0", "flag": "ro"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"off", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x00", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x00"}, + { "attr_name":"green", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x00", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x01"}, + { "attr_name":"amber", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x00", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x02"}, + { "attr_name":"amber_blink_1hz", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x00", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x03"}, + { "attr_name":"amber_blink_4hz", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x00", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x04"}, + { "attr_name":"green_blink_1hz", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x00", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x05"}, + { "attr_name":"green_blink_4hz", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x00", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x06"}, + { "attr_name":"both_blink_1hz", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x00", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x07"}, + { "attr_name":"both_blink_4hz", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x00", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x08"}, + { "attr_name":"unknown", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x00", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x09"} + ] + } + } + }, + "ALARM_LED": + { + "dev_info": { "device_type":"LED", "device_name":"ALARM_LED"}, + "dev_attr": { "index":"0", "flag": "ro"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"off", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x01", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x00"}, + { "attr_name":"green", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x01", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x01"}, + { "attr_name":"amber", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x01", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x02"}, + { "attr_name":"amber_blink_1hz", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x01", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x03"}, + { "attr_name":"amber_blink_4hz", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x01", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x04"}, + { "attr_name":"green_blink_1hz", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x01", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x05"}, + { "attr_name":"green_blink_4hz", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x01", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x06"}, + { "attr_name":"unknown", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x01", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x09"} + ] + } + } + }, + "FANTRAY1_LED": + { + "dev_info": { "device_type":"LED", "device_name":"FANTRAY1_LED"}, + "dev_attr": { "index":"0", "flag": "ro"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"off", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x04", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x00"}, + { "attr_name":"green", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x04", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x01"}, + { "attr_name":"amber", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x04", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x02"}, + { "attr_name":"unknown", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x04", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x03"} + ] + } + } + }, + "FANTRAY2_LED": + { + "dev_info": { "device_type":"LED", "device_name":"FANTRAY2_LED"}, + "dev_attr": { "index":"1", "flag": "ro"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"off", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x05", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x00"}, + { "attr_name":"green", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x05", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x01"}, + { "attr_name":"amber", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x05", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x02"}, + { "attr_name":"unknown", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x05", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x03"} + ] + } + } + }, + "FANTRAY3_LED": + { + "dev_info": { "device_type":"LED", "device_name":"FANTRAY3_LED"}, + "dev_attr": { "index":"2", "flag": "ro"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"off", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x06", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x00"}, + { "attr_name":"green", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x06", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x01"}, + { "attr_name":"amber", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x06", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x02"}, + { "attr_name":"unknown", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x06", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x03"} + ] + } + } + }, + "FANTRAY4_LED": + { + "dev_info": { "device_type":"LED", "device_name":"FANTRAY4_LED"}, + "dev_attr": { "index":"3", "flag": "ro"}, + "bmc": { + "ipmitool" : { + "attr_list": + [ + { "attr_name":"off", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x07", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x00"}, + { "attr_name":"green", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x07", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x01"}, + { "attr_name":"amber", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x07", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x02"}, + { "attr_name":"unknown", "bmc_cmd":"ipmitool raw 0x3A 0x39 0x01 0x07", "raw": "1", "type":"mask", "mask" : "0xff", "descr" :"OFF", "value" : "0x03"} + ] + } + } + } +} diff --git a/device/celestica/x86_64-cel_ds3000-r0/pddf/pddf-device-nonbmc.json b/device/celestica/x86_64-cel_ds3000-r0/pddf/pddf-device-nonbmc.json new file mode 100644 index 000000000000..f0b77c7a3a66 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/pddf/pddf-device-nonbmc.json @@ -0,0 +1,2216 @@ +{ +"PLATFORM": +{ + "num_psus":2, + "num_fantrays":4, + "num_fans_pertray":2, + "num_ports":33, + "num_temps":6, + "num_components":8, + "pddf_dev_types": + { + "description":" - Below is the list of supported PDDF device types (chip names) for various components. If any component uses some other driver, we will create the client using 'echo > /new_device' method", + "CPLD": + [ + "i2c_cpld" + ], + "PSU": + [ + "psu_pmbus" + ], + "FAN": + [ + "fan_ctrl", + "fan_eeprom", + "fan_cpld" + ], + "PORT_MODULE": + [ + "pddf_xcvr" + ], + "FPGAPCIE": + [ + "fpgapci" + ] + + }, + "std_perm_kos": + [ + "i2c_ismt", + "i2c-i801" + ], + + "std_kos": + [ + "lpc_ich", + "i2c_dev", + "i2c_mux_pca954x", + "optoe", + "mc24lc64t", + "ucd9000", + "mp2975", + "lm75", + "at24" + ], + + "pddf_kos": + [ + "pddf_client_module", + "pddf_cpld_module", + "pddf_cpld_driver", + "pddf_mux_module", + "pddf_fan_module", + "pddf_psu_module", + "pddf_led_module", + "pddf_fpgai2c_module", + "pddf_fpgai2c_driver", + "pddf_xcvr_module", + "pddf_xcvr_driver_module", + "pddf_fpgapci_driver", + "pddf_fpgapci_module" + ], + + "custom_kos": + [ + "pddf_custom_psu_driver_module", + "pddf_custom_fpga_algo" + ] + }, + + "COMPONENT1": + { + "comp_attr":{ "name": "BIOS", "type": "bios", "description": "Basic Input/Output System"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "echo `dmidecode -s bios-version`-`dmidecode -s bios-release-date`" }, + { "attr_name":"update", "cmd": "afulnx_64 {} /p /b /n /me /x /k" } + ] + }, + "COMPONENT2": + { + "comp_attr":{ "name": "BaseBoard_CPLD", "type": "cpld", "description": "Base Board CPLD"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "r=$(cat /sys/devices/platform/baseboard/version) && printf '%d.%d' $(($r>>4)) $(($r&0xf))" }, + { "attr_name":"update", "cmd": "ispvm {}" } + ] + }, + "COMPONENT3": + { + "comp_attr":{ "name": "SwitchBoard_CPLD1", "type": "cpld", "description":"Switch Board CPLD"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "r=$(i2cget -y -f 102 0x30 0) && printf '%d.%d' $(($r>>4)) $(($r&0xf))" } + ] + }, + "COMPONENT4": + { + "comp_attr":{ "name": "SwitchBoard_CPLD2", "type": "cpld", "description":"Switch Board CPLD"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "r=$(i2cget -y -f 102 0x31 0) && printf '%d.%d' $(($r>>4)) $(($r&0xf))" } + ] + }, + "COMPONENT5": + { + "comp_attr":{ "name": "COMeBoard_CPLD", "type": "cpld", "description": "COMe Board CPLD"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "r=$(cat /sys/devices/platform/baseboard/come_cpld_version) && printf '%d.%d' $(($r>>4)) $(($r&0xf))" } + ] + }, + "COMPONENT6": + { + "comp_attr":{ "name": "FPGA", "type": "fpga", "description": "Baseboard FPGA"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "r=$(cat /sys/devices/platform/fpga_sysfs/version) && printf '%d.%d' $(($r>>16)) $(($r&0xffff))" }, + { "attr_name":"update", "cmd": "fpga_prog /sys/bus/pci/devices/0000:06:00.0/resource0 {}" } + ] + }, + "COMPONENT7": + { + "comp_attr":{ "name": "PCIe", "type": "pcie", "description":"ASIC PCIe Firmware"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "bcmcmd 'pciephy fw version' | grep 'PCIe FW version' | cut -d ' ' -f 4" } + ] + }, + "COMPONENT8": + { + "comp_attr":{ "name": "SSD", "type": "ssd", "description":"SSD firmware version"}, + "attr_list": + [ + { "attr_name":"version", "get_cmd": "ssdutil -v | grep 'Firmware' | awk '{ print $3 }'" } + ] + }, + + "SYSTEM": + { + "dev_info": {"device_type":"CPU", "device_name":"ROOT_COMPLEX", "device_parent":null}, + "i2c": + { + "CONTROLLERS": + [ + { "dev_name":"i2c-0", "dev":"SMBUS0" }, + { "dev_name":"i2c-1", "dev":"SMBUS1" }, + { "dev_name":"pcie-0", "dev":"PCIE0" } + ] + } + }, + + "SMBUS0": + { + "dev_info": {"device_type": "SMBUS", "device_name": "SMBUS0", "device_parent": "SYSTEM"}, + "i2c": + { + "topo_info": {"dev_addr": "0x0"}, + "DEVICES": + [ + {"dev": "EEPROM1"} + ] + } + }, + + "EEPROM1": + { + "dev_info": {"device_type": "EEPROM", "device_name": "EEPROM1", "device_parent": "SMBUS0"}, + "i2c": + { + "topo_info": {"parent_bus": "0x0", "dev_addr": "0x56", "dev_type": "24lc64t"}, + "dev_attr": {"access_mode": "BLOCK"}, + "attr_list": [ + {"attr_name": "eeprom"} + ] + } + }, + + "SMBUS1": + { + "dev_info": {"device_type": "SMBUS", "device_name": "SMBUS1", "device_parent": "SYSTEM"}, + "i2c": + { + "topo_info": {"dev_addr": "0x1"}, + "DEVICES": + [ + {"dev": "COME_CPLD"} + ] + } + }, + + "COME_CPLD": + { + "dev_info": {"device_type": "CPLD", "device_name": "COME_CPLD", "device_parent": "SMBUS1"}, + "i2c": + { + "topo_info": {"parent_bus": "0x1", "dev_addr": "0x0d", "dev_type": "i2c_cpld"}, + "dev_attr": {} + } + }, + + "PCIE0": + { + "dev_info": {"device_type": "PCIE", "device_name": "PCIE0", "device_parent": "SYSTEM"}, + "i2c": + { + "DEVICES": + [ + {"dev": "FPGAPCIE0"} + ] + } + }, + + "FPGAPCIE0": + { + "dev_info": {"device_type": "FPGAPCIE", "device_name": "FPGAPCIE0", "device_parent": "PCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x0"}, + "dev_attr": { "vendor_id":"0x10EE", "device_id": "0x7021", "virt_bus": "0x64", "data_base_offset":"0x0", "data_size":"0x25000", "i2c_ch_base_offset":"0x10000", "i2c_ch_size":"0x1000", "virt_i2c_ch":"0xc"}, + "channel": + [ + { "chn":"2", "dev":"MUX1" }, + { "chn":"2", "dev":"MUX2" }, + { "chn":"2", "dev":"MUX3" }, + { "chn":"2", "dev":"MUX4" }, + { "chn":"1", "dev":"MUX5" }, + { "chn":"3", "dev":"CPLD_S1" }, + { "chn":"3", "dev":"CPLD_S2" }, + { "chn":"4", "dev":"EEPROM_COME" }, + { "chn":"4", "dev":"CPLD_COME" }, + { "chn":"5", "dev":"EEPROM_BASEBOARD" }, + { "chn":"6", "dev":"FAN-CTRL" }, + { "chn":"6", "dev":"CPLD_BASEBOARD" }, + { "chn":"7", "dev":"MUX6" }, + { "chn":"10", "dev":"TEMP1" }, + { "chn":"10", "dev":"TEMP2" }, + { "chn":"10", "dev":"TEMP3" }, + { "chn":"10", "dev":"TEMP4" }, + { "chn":"10", "dev":"TEMP5" }, + { "chn":"10", "dev":"TEMP6" }, + { "chn":"11", "dev":"MUX7" } + ] + } + }, + + "TEMP1": + { + "dev_info": { "device_type":"TEMP_SENSOR", "device_name":"TEMP1", "device_parent":"FPGAPCIE0"}, + "dev_attr": { "display_name":"Base_Temp_U5"}, + "i2c": + { + "topo_info": { "parent_bus":"0x6d", "dev_addr":"0x4d", "dev_type":"lm75"}, + "attr_list": + [ + { "attr_name": "temp1_high_threshold", "drv_attr_name":"temp1_max"}, + { "attr_name": "temp1_max_hyst"}, + { "attr_name": "temp1_input"} + ] + } + }, + "TEMP2": + { + "dev_info": { "device_type":"TEMP_SENSOR", "device_name":"TEMP2", "device_parent":"FPGAPCIE0"}, + "dev_attr": { "display_name":"Base_Temp_U56"}, + "i2c": + { + "topo_info": { "parent_bus":"0x6d", "dev_addr":"0x4e", "dev_type":"lm75"}, + "attr_list": + [ + { "attr_name": "temp1_high_threshold", "drv_attr_name":"temp1_max"}, + { "attr_name": "temp1_max_hyst"}, + { "attr_name": "temp1_input"} + ] + } + }, + "TEMP3": + { + "dev_info": { "device_type":"TEMP_SENSOR", "device_name":"TEMP3", "device_parent":"FPGAPCIE0"}, + "dev_attr": { "display_name":"Switch_Temp_U17"}, + "i2c": + { + "topo_info": { "parent_bus":"0x6d", "dev_addr":"0x4c", "dev_type":"lm75"}, + "attr_list": + [ + { "attr_name": "temp1_high_threshold", "drv_attr_name":"temp1_max"}, + { "attr_name": "temp1_max_hyst"}, + { "attr_name": "temp1_input"} + ] + } + }, + "TEMP4": + { + "dev_info": { "device_type":"TEMP_SENSOR", "device_name":"TEMP4", "device_parent":"FPGAPCIE0"}, + "dev_attr": { "display_name":"Switch_Temp_U18"}, + "i2c": + { + "topo_info": { "parent_bus":"0x6d", "dev_addr":"0x49", "dev_type":"lm75"}, + "attr_list": + [ + { "attr_name": "temp1_high_threshold", "drv_attr_name":"temp1_max"}, + { "attr_name": "temp1_max_hyst"}, + { "attr_name": "temp1_input"} + ] + } + }, + "TEMP5": + { + "dev_info": { "device_type":"TEMP_SENSOR", "device_name":"TEMP5", "device_parent":"FPGAPCIE0"}, + "dev_attr": { "display_name":"Switch_Temp_U28"}, + "i2c": + { + "topo_info": { "parent_bus":"0x6d", "dev_addr":"0x4a", "dev_type":"lm75"}, + "attr_list": + [ + { "attr_name": "temp1_high_threshold", "drv_attr_name":"temp1_max"}, + { "attr_name": "temp1_max_hyst"}, + { "attr_name": "temp1_input"} + ] + } + }, + "TEMP6": + { + "dev_info": { "device_type":"TEMP_SENSOR", "device_name":"TEMP6", "device_parent":"FPGAPCIE0"}, + "dev_attr": { "display_name":"Switch_Temp_U29"}, + "i2c": + { + "topo_info": { "parent_bus":"0x6d", "dev_addr":"0x4b", "dev_type":"lm75"}, + "attr_list": + [ + { "attr_name": "temp1_high_threshold", "drv_attr_name":"temp1_max"}, + { "attr_name": "temp1_max_hyst"}, + { "attr_name": "temp1_input"} + ] + } + }, + + "CPLD_BASEBOARD": + { + "dev_info": { "device_type":"CPLD", "device_name":"CPLD_BASEBOARD", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x69", "dev_addr":"0x0d", "dev_type":"i2c_cpld"}, + "dev_attr":{} + } + }, + + "EEPROM_BASEBOARD": + { + "dev_info": {"device_type": "EEPROM", "device_name": "EEPROM_BASEBOARD", "device_parent": "FPGAPCIE0"}, + "i2c": + { + "topo_info": {"parent_bus": "0x68", "dev_addr": "0x57", "dev_type": "24lc64t"}, + "dev_attr": {"access_mode": "BLOCK"}, + "attr_list": [ + {"attr_name": "eeprom"} + ] + } + }, + + "EEPROM_COME": + { + "dev_info": {"device_type": "EEPROM", "device_name": "EEPROM_COME", "device_parent": "FPGAPCIE0"}, + "i2c": + { + "topo_info": {"parent_bus": "0x67", "dev_addr": "0x50", "dev_type": "24lc64t"}, + "dev_attr": {"access_mode": "BLOCK"}, + "attr_list": [ + {"attr_name": "eeprom"} + ] + } + }, + + "CPLD_COME": + { + "dev_info": { "device_type":"CPLD", "device_name":"CPLD_COME", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x67", "dev_addr":"0x0d", "dev_type":"i2c_cpld"}, + "dev_attr":{} + } + }, + + "CPLD_S1": + { + "dev_info": { "device_type":"CPLD", "device_name":"CPLD_S1", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x66", "dev_addr":"0x30", "dev_type":"i2c_cpld"}, + "dev_attr":{} + } + }, + + "CPLD_S2": + { + "dev_info": { "device_type":"CPLD", "device_name":"CPLD_S2", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x66", "dev_addr":"0x31", "dev_type":"i2c_cpld"}, + "dev_attr":{} + } + }, + + "MUX1": + { + "dev_info": { "device_type":"MUX", "device_name":"MUX1", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x65", "dev_addr":"0x72", "dev_type":"pca9548"}, + "dev_attr": { "virt_bus":"0x2", "idle_state":"-2" }, + "channel": + [ + { "chn":"0", "dev":"PORT1" }, + { "chn":"1", "dev":"PORT2" }, + { "chn":"2", "dev":"PORT3" }, + { "chn":"3", "dev":"PORT4" }, + { "chn":"4", "dev":"PORT5" }, + { "chn":"5", "dev":"PORT6" }, + { "chn":"6", "dev":"PORT7" }, + { "chn":"7", "dev":"PORT8" } + ] + } + }, + + "MUX2": + { + "dev_info": { "device_type":"MUX", "device_name":"MUX2", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x65", "dev_addr":"0x73", "dev_type":"pca9548"}, + "dev_attr": { "virt_bus":"0xa", "idle_state":"-2" }, + "channel": + [ + { "chn":"0", "dev":"PORT9" }, + { "chn":"1", "dev":"PORT10" }, + { "chn":"2", "dev":"PORT11" }, + { "chn":"3", "dev":"PORT12" }, + { "chn":"4", "dev":"PORT13" }, + { "chn":"5", "dev":"PORT14" }, + { "chn":"6", "dev":"PORT15" }, + { "chn":"7", "dev":"PORT16" } + ] + } + }, + + "MUX3": + { + "dev_info": { "device_type":"MUX", "device_name":"MUX3", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x65", "dev_addr":"0x74", "dev_type":"pca9548"}, + "dev_attr": { "virt_bus":"0x12", "idle_state":"-2" }, + "channel": + [ + { "chn":"0", "dev":"PORT17" }, + { "chn":"1", "dev":"PORT18" }, + { "chn":"2", "dev":"PORT19" }, + { "chn":"3", "dev":"PORT20" }, + { "chn":"4", "dev":"PORT21" }, + { "chn":"5", "dev":"PORT22" }, + { "chn":"6", "dev":"PORT23" }, + { "chn":"7", "dev":"PORT24" } + ] + } + }, + + "MUX4": + { + "dev_info": { "device_type":"MUX", "device_name":"MUX4", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x65", "dev_addr":"0x75", "dev_type":"pca9548"}, + "dev_attr": { "virt_bus":"0x1a", "idle_state":"-2" }, + "channel": + [ + { "chn":"0", "dev":"PORT25" }, + { "chn":"1", "dev":"PORT26" }, + { "chn":"2", "dev":"PORT27" }, + { "chn":"3", "dev":"PORT28" }, + { "chn":"4", "dev":"PORT29" }, + { "chn":"5", "dev":"PORT30" }, + { "chn":"6", "dev":"PORT31" }, + { "chn":"7", "dev":"PORT32" } + ] + } + }, + "MUX5": + { + "dev_info": { "device_type":"MUX", "device_name":"MUX5", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x64", "dev_addr":"0x72", "dev_type":"pca9548"}, + "dev_attr": { "virt_bus":"0x22"}, + "channel": + [ + { "chn":"0", "dev":"PORT33" } + ] + } + }, + + "MUX6": + { + "dev_info": { "device_type":"MUX", "device_name":"MUX6", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x6a", "dev_addr":"0x70", "dev_type":"pca9548"}, + "dev_attr": { "virt_bus":"0x2a", "idle_state":"-2" }, + "channel": + [ + { "chn":"0", "dev":"PSU1" }, + { "chn":"0", "dev":"PSU1-EEPROM" }, + { "chn":"1", "dev":"PSU2" }, + { "chn":"1", "dev":"PSU2-EEPROM" } + ] + } + }, + + "MUX7": + { + "dev_info": { "device_type":"MUX", "device_name":"MUX7", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x6e", "dev_addr":"0x77", "dev_type":"pca9548"}, + "dev_attr": { "virt_bus":"0x32", "idle_state":"-2" }, + "channel": + [ + { "chn":"0", "dev":"FANTRAY1_EEPROM" }, + { "chn":"1", "dev":"FANTRAY2_EEPROM" }, + { "chn":"3", "dev":"FANTRAY3_EEPROM" }, + { "chn":"4", "dev":"FANTRAY4_EEPROM" } + ] + } + }, + + "PORT1": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT1", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"1"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT1-EEPROM" }, + { "itf":"control", "dev":"PORT1-CTRL" } + + ] + } + }, + "PORT1-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT1-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT1"}, + "i2c": + { + "topo_info": { "parent_bus":"0x2", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + "PORT1-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT1-CTRL", "device_parent":"MUX1", "virt_parent":"PORT1"}, + "i2c": + { + "topo_info": { "parent_bus":"0x2", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + "PORT2": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT2", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"2"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT2-EEPROM" }, + { "itf":"control", "dev":"PORT2-CTRL" } + ] + } + }, + + "PORT2-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT2-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT2"}, + "i2c": + { + "topo_info": { "parent_bus":"0x3", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT2-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT2-CTRL", "device_parent":"MUX1", "virt_parent":"PORT2"}, + "i2c": + { + "topo_info": { "parent_bus":"0x3", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x10", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x10", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x10", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x10", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT3": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT3", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"3"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT3-EEPROM" }, + { "itf":"control", "dev":"PORT3-CTRL" } + ] + } + }, + "PORT3-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT3-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT3"}, + "i2c": + { + "topo_info": { "parent_bus":"0x4", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT3-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT3-CTRL", "device_parent":"MUX1", "virt_parent":"PORT3"}, + "i2c": + { + "topo_info": { "parent_bus":"0x4", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x20", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x20", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x20", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x20", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT4": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT4", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"4"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT4-EEPROM" }, + { "itf":"control", "dev":"PORT4-CTRL" } + ] + } + }, + "PORT4-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT4-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT4"}, + "i2c": + { + "topo_info": { "parent_bus":"0x5", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT4-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT4-CTRL", "device_parent":"MUX1", "virt_parent":"PORT4"}, + "i2c": + { + "topo_info": { "parent_bus":"0x5", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x30", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x30", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x30", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x30", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT5": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT5", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"5"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT5-EEPROM" }, + { "itf":"control", "dev":"PORT5-CTRL" } + ] + } + }, + + "PORT5-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT5-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT5"}, + "i2c": + { + "topo_info": { "parent_bus":"0x6", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT5-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT5-CTRL", "device_parent":"MUX1", "virt_parent":"PORT5"}, + "i2c": + { + "topo_info": { "parent_bus":"0x6", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x40", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x40", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x40", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x40", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT6": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT6", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"6"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT6-EEPROM" }, + { "itf":"control", "dev":"PORT6-CTRL" } + ] + } + }, + "PORT6-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT6-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT6"}, + "i2c": + { + "topo_info": { "parent_bus":"0x7", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT6-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT6-CTRL", "device_parent":"MUX1", "virt_parent":"PORT6"}, + "i2c": + { + "topo_info": { "parent_bus":"0x7", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x50", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x50", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x50", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x50", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT7": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT7", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"7"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT7-EEPROM" }, + { "itf":"control", "dev":"PORT7-CTRL" } + ] + } + }, + "PORT7-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT7-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT7"}, + "i2c": + { + "topo_info": { "parent_bus":"0x8", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT7-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT7-CTRL", "device_parent":"MUX1", "virt_parent":"PORT7"}, + "i2c": + { + "topo_info": { "parent_bus":"0x8", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x60", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x60", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x60", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x60", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT8": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT8", "device_parent":"MUX1"}, + "dev_attr": { "dev_idx":"8"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT8-EEPROM" }, + { "itf":"control", "dev":"PORT8-CTRL" } + ] + } + }, + "PORT8-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT8-EEPROM", "device_parent":"MUX1", "virt_parent":"PORT8"}, + "i2c": + { + "topo_info": { "parent_bus":"0x9", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT8-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT8-CTRL", "device_parent":"MUX1", "virt_parent":"PORT8"}, + "i2c": + { + "topo_info": { "parent_bus":"0x9", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x70", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x70", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x70", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x70", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT9": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT9", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"9"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT9-EEPROM" }, + { "itf":"control", "dev":"PORT9-CTRL" } + ] + } + }, + "PORT9-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT9-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT9"}, + "i2c": + { + "topo_info": { "parent_bus":"0xa", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT9-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT9-CTRL", "device_parent":"MUX2", "virt_parent":"PORT9"}, + "i2c": + { + "topo_info": { "parent_bus":"0xa", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x80", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x80", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x80", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x80", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT10": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT10", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"10"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT10-EEPROM" }, + { "itf":"control", "dev":"PORT10-CTRL" } + ] + } + }, + "PORT10-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT10-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT10"}, + "i2c": + { + "topo_info": { "parent_bus":"0xb", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT10-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT10-CTRL", "device_parent":"MUX2", "virt_parent":"PORT10"}, + "i2c": + { + "topo_info": { "parent_bus":"0xb", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x90", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x90", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x90", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x90", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT11": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT11", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"11"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT11-EEPROM" }, + { "itf":"control", "dev":"PORT11-CTRL" } + ] + } + }, + "PORT11-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT11-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT11"}, + "i2c": + { + "topo_info": { "parent_bus":"0xc", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT11-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT11-CTRL", "device_parent":"MUX2", "virt_parent":"PORT11"}, + "i2c": + { + "topo_info": { "parent_bus":"0xc", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xa0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xa0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xa0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xa0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT12": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT12", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"12"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT12-EEPROM" }, + { "itf":"control", "dev":"PORT12-CTRL" } + ] + } + }, + "PORT12-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT12-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT12"}, + "i2c": + { + "topo_info": { "parent_bus":"0xd", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT12-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT12-CTRL", "device_parent":"MUX2", "virt_parent":"PORT12"}, + "i2c": + { + "topo_info": { "parent_bus":"0xd", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xb0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xb0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xb0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xb0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT13": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT13", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"13"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT13-EEPROM" }, + { "itf":"control", "dev":"PORT13-CTRL" } + ] + } + }, + "PORT13-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT13-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT13"}, + "i2c": + { + "topo_info": { "parent_bus":"0xe", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT13-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT13-CTRL", "device_parent":"MUX2", "virt_parent":"PORT13"}, + "i2c": + { + "topo_info": { "parent_bus":"0xe", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xc0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xc0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xc0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xc0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT14": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT14", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"14"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT14-EEPROM" }, + { "itf":"control", "dev":"PORT14-CTRL" } + ] + } + }, + "PORT14-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT14-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT14"}, + "i2c": + { + "topo_info": { "parent_bus":"0xF", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT14-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT14-CTRL", "device_parent":"MUX2", "virt_parent":"PORT14"}, + "i2c": + { + "topo_info": { "parent_bus":"0xf", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xd0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xd0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xd0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xd0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT15": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT15", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"15"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT15-EEPROM" }, + { "itf":"control", "dev":"PORT15-CTRL" } + ] + } + }, + "PORT15-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT15-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT15"}, + "i2c": + { + "topo_info": { "parent_bus":"0x10", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT15-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT15-CTRL", "device_parent":"MUX2", "virt_parent":"PORT15"}, + "i2c": + { + "topo_info": { "parent_bus":"0x10", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xe0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xe0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xe0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xe0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT16": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT16", "device_parent":"MUX2"}, + "dev_attr": { "dev_idx":"16"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT16-EEPROM" }, + { "itf":"control", "dev":"PORT16-CTRL" } + ] + } + }, + "PORT16-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT16-EEPROM", "device_parent":"MUX2", "virt_parent":"PORT16"}, + "i2c": + { + "topo_info": { "parent_bus":"0x11", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT16-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT16-CTRL", "device_parent":"MUX2", "virt_parent":"PORT16"}, + "i2c": + { + "topo_info": { "parent_bus":"0x11", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xf0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xf0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xf0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0xf0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT17": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT17", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"17"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT17-EEPROM" }, + { "itf":"control", "dev":"PORT17-CTRL" } + ] + } + }, + "PORT17-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT17-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT17"}, + "i2c": + { + "topo_info": { "parent_bus":"0x12", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT17-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT17-CTRL", "device_parent":"MUX3", "virt_parent":"PORT17"}, + "i2c": + { + "topo_info": { "parent_bus":"0x12", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x100", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x100", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x100", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x100", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT18": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT18", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"18"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT18-EEPROM" }, + { "itf":"control", "dev":"PORT18-CTRL" } + ] + } + }, + "PORT18-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT18-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT18"}, + "i2c": + { + "topo_info": { "parent_bus":"0x13", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT18-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT18-CTRL", "device_parent":"MUX3", "virt_parent":"PORT18"}, + "i2c": + { + "topo_info": { "parent_bus":"0x13", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x110", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x110", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x110", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x110", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT19": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT19", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"19"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT19-EEPROM" }, + { "itf":"control", "dev":"PORT19-CTRL" } + ] + } + }, + "PORT19-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT19-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT19"}, + "i2c": + { + "topo_info": { "parent_bus":"0x14", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT19-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT19-CTRL", "device_parent":"MUX3", "virt_parent":"PORT19"}, + "i2c": + { + "topo_info": { "parent_bus":"0x14", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x120", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x120", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x120", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x120", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT20": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT20", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"20"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT20-EEPROM" }, + { "itf":"control", "dev":"PORT20-CTRL" } + ] + } + }, + "PORT20-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT20-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT20"}, + "i2c": + { + "topo_info": { "parent_bus":"0x15", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT20-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT20-CTRL", "device_parent":"MUX3", "virt_parent":"PORT20"}, + "i2c": + { + "topo_info": { "parent_bus":"0x15", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x130", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x130", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x130", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x130", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT21": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT21", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"21"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT21-EEPROM" }, + { "itf":"control", "dev":"PORT21-CTRL" } + ] + } + }, + "PORT21-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT21-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT21"}, + "i2c": + { + "topo_info": { "parent_bus":"0x16", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT21-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT21-CTRL", "device_parent":"MUX3", "virt_parent":"PORT21"}, + "i2c": + { + "topo_info": { "parent_bus":"0x16", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x140", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x140", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x140", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x140", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT22": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT22", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"22"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT22-EEPROM" }, + { "itf":"control", "dev":"PORT22-CTRL" } + ] + } + }, + "PORT22-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT22-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT22"}, + "i2c": + { + "topo_info": { "parent_bus":"0x17", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT22-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT22-CTRL", "device_parent":"MUX3", "virt_parent":"PORT22"}, + "i2c": + { + "topo_info": { "parent_bus":"0x17", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x150", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x150", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x150", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x150", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT23": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT23", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"23"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT23-EEPROM" }, + { "itf":"control", "dev":"PORT23-CTRL" } + ] + } + }, + "PORT23-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT23-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT23"}, + "i2c": + { + "topo_info": { "parent_bus":"0x18", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT23-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT23-CTRL", "device_parent":"MUX3", "virt_parent":"PORT23"}, + "i2c": + { + "topo_info": { "parent_bus":"0x18", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x160", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x160", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x160", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x160", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT24": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT24", "device_parent":"MUX3"}, + "dev_attr": { "dev_idx":"24"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT24-EEPROM" }, + { "itf":"control", "dev":"PORT24-CTRL" } + ] + } + }, + "PORT24-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT24-EEPROM", "device_parent":"MUX3", "virt_parent":"PORT24"}, + "i2c": + { + "topo_info": { "parent_bus":"0x19", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT24-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT24-CTRL", "device_parent":"MUX3", "virt_parent":"PORT24"}, + "i2c": + { + "topo_info": { "parent_bus":"0x19", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x170", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x170", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x170", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x170", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT25": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT25", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"25"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT25-EEPROM" }, + { "itf":"control", "dev":"PORT25-CTRL" } + ] + } + }, + "PORT25-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT25-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT25"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1a", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT25-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT25-CTRL", "device_parent":"MUX4", "virt_parent":"PORT25"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1a", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x180", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x180", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x180", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x180", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT26": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT26", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"26"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT26-EEPROM" }, + { "itf":"control", "dev":"PORT26-CTRL" } + ] + } + }, + "PORT26-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT26-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT26"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1b", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + + "PORT26-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT26-CTRL", "device_parent":"MUX4", "virt_parent":"PORT26"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1b", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x190", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x190", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x190", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x190", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT27": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT27", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"27"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT27-EEPROM" }, + { "itf":"control", "dev":"PORT27-CTRL" } + ] + } + }, + "PORT27-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT27-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT27"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1c", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT27-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT27-CTRL", "device_parent":"MUX4", "virt_parent":"PORT27"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1c", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1a0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1a0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1a0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1a0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT28": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT28", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"28"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT28-EEPROM" }, + { "itf":"control", "dev":"PORT28-CTRL" } + ] + } + }, + "PORT28-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT28-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT28"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1d", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT28-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT28-CTRL", "device_parent":"MUX4", "virt_parent":"PORT28"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1d", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1b0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1b0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1b0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1b0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT29": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT29", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"29"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT29-EEPROM" }, + { "itf":"control", "dev":"PORT29-CTRL" } + ] + } + }, + "PORT29-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT29-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT29"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1e", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT29-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT29-CTRL", "device_parent":"MUX4", "virt_parent":"PORT29"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1e", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1c0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1c0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1c0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1c0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT30": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT30", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"30"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT30-EEPROM" }, + { "itf":"control", "dev":"PORT30-CTRL" } + ] + } + }, + "PORT30-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT30-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT30"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1f", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT30-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT30-CTRL", "device_parent":"MUX4", "virt_parent":"PORT30"}, + "i2c": + { + "topo_info": { "parent_bus":"0x1f", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1d0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1d0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1d0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1d0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT31": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT31", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"31"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT31-EEPROM" }, + { "itf":"control", "dev":"PORT31-CTRL" } + ] + } + }, + "PORT31-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT31-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT31"}, + "i2c": + { + "topo_info": { "parent_bus":"0x20", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT31-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT31-CTRL", "device_parent":"MUX4", "virt_parent":"PORT31"}, + "i2c": + { + "topo_info": { "parent_bus":"0x20", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1e0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1e0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1e0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1e0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + + "PORT32": + { + "dev_info": { "device_type":"QSFP28", "device_name":"PORT32", "device_parent":"MUX4"}, + "dev_attr": { "dev_idx":"32"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT32-EEPROM" }, + { "itf":"control", "dev":"PORT32-CTRL" } + ] + } + }, + "PORT32-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT32-EEPROM", "device_parent":"MUX4", "virt_parent":"PORT32"}, + "i2c": + { + "topo_info": { "parent_bus":"0x21", "dev_addr":"0x50", "dev_type":"optoe1"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT32-CTRL": + { + "dev_info": { "device_type":"pci", "device_name":"PORT32-CTRL", "device_parent":"MUX4", "virt_parent":"PORT32"}, + "i2c": + { + "topo_info": { "parent_bus":"0x21", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1f0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_reset", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1f0", "attr_mask":"0x4", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_lpmode", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1f0", "attr_mask":"0x6", "attr_cmpval":"0x40", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x1f0", "attr_mask":"0x4", "attr_cmpval":"0x10", "attr_len":"1"} + ] + } + }, + + "PORT33": + { + "dev_info": { "device_type":"SFP+", "device_name":"PORT33", "device_parent":"MUX5"}, + "dev_attr": { "dev_idx":"33"}, + "i2c": + { + "interface": + [ + { "itf":"eeprom", "dev":"PORT33-EEPROM" }, + { "itf":"control", "dev":"PORT33-CTRL" } + ] + } + }, + "PORT33-EEPROM": + { + "dev_info": { "device_type":"", "device_name":"PORT33-EEPROM", "device_parent":"MUX5", "virt_parent":"PORT33"}, + "i2c": + { + "topo_info": { "parent_bus":"0x22", "dev_addr":"0x50", "dev_type":"optoe2"}, + "attr_list": + [ + { "attr_name":"eeprom"} + ] + } + }, + + "PORT33-CTRL": + { + "dev_info": { "device_type":"", "device_name":"PORT33-CTRL", "device_parent":"MUX5", "virt_parent":"PORT33"}, + "i2c": + { + "topo_info": { "parent_bus":"0x22", "dev_addr":"0x53", "dev_type":"pddf_xcvr"}, + "attr_list": + [ + { "attr_name":"xcvr_present", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x200", "attr_mask":"0x0", "attr_cmpval":"0x0", "attr_len":"1"}, + { "attr_name":"xcvr_txfault", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x200", "attr_mask":"0x2", "attr_cmpval":"0x4", "attr_len":"1"}, + { "attr_name":"xcvr_txdisable", "attr_devaddr":"0x1010", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x200", "attr_mask":"0x0", "attr_cmpval":"0x1", "attr_len":"1"}, + { "attr_name":"xcvr_rxlos", "attr_devaddr":"0x1014", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x200", "attr_mask":"0x1", "attr_cmpval":"0x2", "attr_len":"1"}, + { "attr_name":"xcvr_intr_status", "attr_devaddr":"0x1018", "attr_devtype":"fpgapci", "attr_devname":"FPGAPCIE0", "attr_offset":"0x200", "attr_mask":"0x0", "attr_cmpval":"0x1", "attr_len":"1"} + ] + } + }, + + "PSU1": + { + "dev_info": { "device_type":"PSU", "device_name":"PSU1", "device_parent":"MUX6"}, + "dev_attr": { "dev_idx":"1", "num_psu_fans": "1"}, + "i2c": + { + "interface": + [ + { "itf":"pmbus", "dev":"PSU1-PMBUS" } + ] + } + }, + "PSU1-PMBUS": + { + "dev_info": { "device_type":"PSU-PMBUS", "device_name":"PSU1-PMBUS", "device_parent":"MUX6", "virt_parent":"PSU1" }, + "i2c": + { + "topo_info":{ "parent_bus":"0x2a", "dev_addr":"0x5a", "dev_type":"psu_pmbus"}, + "attr_list": + [ + {"attr_name":"psu_present", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0x60", "attr_mask":"0x08", "attr_cmpval":"0x00", "attr_len":"1"}, + {"attr_name":"psu_power_good", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0x60", "attr_mask":"0x02", "attr_cmpval":"0x02", "attr_len":"1"}, + {"attr_name":"psu_model_name", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0x9a", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"17"}, + {"attr_name":"psu_serial_num", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0x9e", "attr_mask":"0x0", "attr_cmpval":"0x00", "attr_len":"13"}, + {"attr_name":"psu_mfr_id", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0x99", "attr_mask":"0x0", "attr_cmpval":"0x00", "attr_len":"7"}, + {"attr_name":"psu_fan_dir", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0x9a", "attr_mask":"0x0", "attr_cmpval":"0x00", "attr_len":"1"}, + {"attr_name":"psu_p_out", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0x96", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_v_out", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0x8b", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_i_out", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0x8c", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_p_in", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0x97", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_v_in", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0x88", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_i_in", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0x89", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_fan1_speed_rpm", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0x90", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_temp1_input", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0x8e", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_temp1_high_threshold", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0xc0", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_v_out_max", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0xa5", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_v_out_min", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0xa4", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_p_out_max", "attr_devaddr":"0x5a", "attr_devtype":"pmbus", "attr_offset":"0xa7", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"} + ] + } + }, + "PSU1-EEPROM": + { + "dev_info": {"device_type": "EEPROM", "device_name": "PSU1-EEPROM", "device_parent": "MUX6"}, + "i2c": + { + "topo_info": {"parent_bus": "0x2a", "dev_addr": "0x52", "dev_type": "24lc64t"}, + "dev_attr": {"access_mode": "BLOCK"}, + "attr_list": [ + {"attr_name": "eeprom"} + ] + } + }, + + "PSU2": + { + "dev_info": { "device_type":"PSU", "device_name":"PSU2", "device_parent":"MUX6"}, + "dev_attr": { "dev_idx":"2", "num_psu_fans": "1"}, + "i2c": + { + "interface": + [ + { "itf":"pmbus", "dev":"PSU2-PMBUS" } + ] + } + }, + "PSU2-PMBUS": + { + "dev_info": { "device_type":"PSU-PMBUS", "device_name":"PSU2-PMBUS", "device_parent":"MUX6", "virt_parent":"PSU2" }, + "i2c": + { + "topo_info":{ "parent_bus":"0x2b", "dev_addr":"0x5b", "dev_type":"psu_pmbus"}, + "attr_list": + [ + {"attr_name":"psu_present", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0x60", "attr_mask":"0x04", "attr_cmpval":"0x00", "attr_len":"1"}, + {"attr_name":"psu_power_good", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0x60", "attr_mask":"0x01", "attr_cmpval":"0x01", "attr_len":"1"}, + {"attr_name":"psu_model_name", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0x9a", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"17"}, + {"attr_name":"psu_serial_num", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0x9e", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"13"}, + {"attr_name":"psu_mfr_id", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0x99", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"7"}, + {"attr_name":"psu_fan_dir", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0x9a", "attr_mask":"0x0", "attr_cmpval":"0x00", "attr_len":"1"}, + {"attr_name":"psu_p_out", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0x96", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_v_out", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0x8b", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_i_out", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0x8c", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_p_in", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0x97", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_v_in", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0x88", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_i_in", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0x89", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_fan1_speed_rpm", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0x90", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_temp1_input", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0x8e", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_temp1_high_threshold", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0xc0", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_v_out_max", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0xa5", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_v_out_min", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0xa4", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"}, + {"attr_name":"psu_p_out_max", "attr_devaddr":"0x5b", "attr_devtype":"pmbus", "attr_offset":"0xa7", "attr_mask":"0x0", "attr_cmpval":"0xff", "attr_len":"2"} + ] + } + }, + "PSU2-EEPROM": + { + "dev_info": {"device_type": "EEPROM", "device_name": "PSU2-EEPROM", "device_parent": "MUX6"}, + "i2c": + { + "topo_info": {"parent_bus": "0x2b", "dev_addr": "0x53", "dev_type": "24lc64t"}, + "dev_attr": {"access_mode": "BLOCK"}, + "attr_list": [ + {"attr_name": "eeprom"} + ] + } + }, + + "FANTRAY1_EEPROM": + { + "dev_info": {"device_type": "EEPROM", "device_name": "FANTRAY1_EEPROM", "device_parent": "MUX7"}, + "i2c": + { + "topo_info": {"parent_bus": "0x32", "dev_addr": "0x50", "dev_type": "24c64"}, + "dev_attr": {"access_mode": "BLOCK"}, + "attr_list": [ + {"attr_name": "eeprom"} + ] + } + }, + "FANTRAY2_EEPROM": + { + "dev_info": {"device_type": "EEPROM", "device_name": "FANTRAY2_EEPROM", "device_parent": "MUX7"}, + "i2c": + { + "topo_info": {"parent_bus": "0x33", "dev_addr": "0x50", "dev_type": "24c64"}, + "dev_attr": {"access_mode": "BLOCK"}, + "attr_list": [ + {"attr_name": "eeprom"} + ] + } + }, + "FANTRAY3_EEPROM": + { + "dev_info": {"device_type": "EEPROM", "device_name": "FANTRAY3_EEPROM", "device_parent": "MUX7"}, + "i2c": + { + "topo_info": {"parent_bus": "0x35", "dev_addr": "0x50", "dev_type": "24c64"}, + "dev_attr": {"access_mode": "BLOCK"}, + "attr_list": [ + {"attr_name": "eeprom"} + ] + } + }, + "FANTRAY4_EEPROM": + { + "dev_info": {"device_type": "EEPROM", "device_name": "FANTRAY4_EEPROM", "device_parent": "MUX7"}, + "i2c": + { + "topo_info": {"parent_bus": "0x36", "dev_addr": "0x50", "dev_type": "24c64"}, + "dev_attr": {"access_mode": "BLOCK"}, + "attr_list": [ + {"attr_name": "eeprom"} + ] + } + }, + + "FAN-CTRL": + { + "dev_info": { "device_type":"FAN", "device_name":"FAN-CTRL", "device_parent":"FPGAPCIE0"}, + "i2c": + { + "topo_info": { "parent_bus":"0x69", "dev_addr":"0x16", "dev_type":"fan_cpld"}, + "dev_attr": { "num_fantrays":"4"}, + "attr_list": + [ + {"attr_name":"fan1_present", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xb4", "attr_mask":"0x01", "attr_cmpval": "0x1", "attr_len":"1"}, + {"attr_name":"fan2_present", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xb4", "attr_mask":"0x01", "attr_cmpval": "0x1", "attr_len":"1"}, + {"attr_name":"fan3_present", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xba", "attr_mask":"0x01", "attr_cmpval": "0x1", "attr_len":"1"}, + {"attr_name":"fan4_present", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xba", "attr_mask":"0x01", "attr_cmpval": "0x1", "attr_len":"1"}, + {"attr_name":"fan5_present", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xc6", "attr_mask":"0x01", "attr_cmpval": "0x1", "attr_len":"1"}, + {"attr_name":"fan6_present", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xc6", "attr_mask":"0x01", "attr_cmpval": "0x1", "attr_len":"1"}, + {"attr_name":"fan7_present", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xcc", "attr_mask":"0x01", "attr_cmpval": "0x1", "attr_len":"1"}, + {"attr_name":"fan8_present", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xcc", "attr_mask":"0x01", "attr_cmpval": "0x1", "attr_len":"1"}, + {"attr_name":"fan1_direction", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xb4", "attr_mask":"0x02", "attr_cmpval": "0x2", "attr_len":"1"}, + {"attr_name":"fan2_direction", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xb4", "attr_mask":"0x02", "attr_cmpval": "0x2", "attr_len":"1"}, + {"attr_name":"fan3_direction", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xba", "attr_mask":"0x02", "attr_cmpval": "0x2", "attr_len":"1"}, + {"attr_name":"fan4_direction", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xba", "attr_mask":"0x02", "attr_cmpval": "0x2", "attr_len":"1"}, + {"attr_name":"fan5_direction", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xc6", "attr_mask":"0x02", "attr_cmpval": "0x2", "attr_len":"1"}, + {"attr_name":"fan6_direction", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xc6", "attr_mask":"0x02", "attr_cmpval": "0x2", "attr_len":"1"}, + {"attr_name":"fan7_direction", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xcc", "attr_mask":"0x02", "attr_cmpval": "0x2", "attr_len":"1"}, + {"attr_name":"fan8_direction", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xcc", "attr_mask":"0x02", "attr_cmpval": "0x2", "attr_len":"1"}, + {"attr_name":"fan1_input", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xb0", "attr_mask":"0xff", "attr_len":"1", "attr_mult":"120", "attr_is_divisor":0}, + {"attr_name":"fan2_input", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xb1", "attr_mask":"0xff", "attr_len":"1", "attr_mult":"120", "attr_is_divisor":0}, + {"attr_name":"fan3_input", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xb6", "attr_mask":"0xff", "attr_len":"1", "attr_mult":"120", "attr_is_divisor":0}, + {"attr_name":"fan4_input", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xb7", "attr_mask":"0xff", "attr_len":"1", "attr_mult":"120", "attr_is_divisor":0}, + {"attr_name":"fan5_input", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xc2", "attr_mask":"0xff", "attr_len":"1", "attr_mult":"120", "attr_is_divisor":0}, + {"attr_name":"fan6_input", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xc3", "attr_mask":"0xff", "attr_len":"1", "attr_mult":"120", "attr_is_divisor":0}, + {"attr_name":"fan7_input", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xc8", "attr_mask":"0xff", "attr_len":"1", "attr_mult":"120", "attr_is_divisor":0}, + {"attr_name":"fan8_input", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xc9", "attr_mask":"0xff", "attr_len":"1", "attr_mult":"120", "attr_is_divisor":0}, + {"attr_name":"fan1_pwm", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xb2", "attr_mask":"0xff", "attr_cmpval": "0x0","attr_len":"1"}, + {"attr_name":"fan2_pwm", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xb2", "attr_mask":"0xff", "attr_cmpval": "0x0","attr_len":"1"}, + {"attr_name":"fan3_pwm", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xb8", "attr_mask":"0xff", "attr_cmpval": "0x0","attr_len":"1"}, + {"attr_name":"fan4_pwm", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xb8", "attr_mask":"0xff", "attr_cmpval": "0x0","attr_len":"1"}, + {"attr_name":"fan5_pwm", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xc4", "attr_mask":"0xff", "attr_cmpval": "0x0","attr_len":"1"}, + {"attr_name":"fan6_pwm", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xc4", "attr_mask":"0xff", "attr_cmpval": "0x0","attr_len":"1"}, + {"attr_name":"fan7_pwm", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xca", "attr_mask":"0xff", "attr_cmpval": "0x0","attr_len":"1"}, + {"attr_name":"fan8_pwm", "attr_devaddr":"0x0d", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "attr_offset":"0xca", "attr_mask":"0xff", "attr_cmpval": "0x0","attr_len":"1"} + ] + } + }, + + "FANTRAY1_LED": + { + "dev_info": { "device_type":"LED", "device_name":"FANTRAY_LED"}, + "dev_attr": { "index":"0"}, + "i2c" : { + "attr_list": + [ + {"attr_name":"off","attr_devtype":"cpld","attr_devname":"CPLD_BASEBOARD","bits":"1:0","descr":"Off","value":"0x03","swpld_addr":"0x0d","swpld_addr_offset":"0xb3"}, + {"attr_name":"green","attr_devtype":"cpld","attr_devname":"CPLD_BASEBOARD","bits":"1:0","descr":"Green","value":"0x02","swpld_addr":"0x0d","swpld_addr_offset":"0xb3"}, + {"attr_name":"amber","attr_devtype":"cpld","attr_devname":"CPLD_BASEBOARD","bits":"1:0","descr":"Amber","value":"0x01","swpld_addr":"0x0d","swpld_addr_offset":"0xb3"} + ] + } + }, + "FANTRAY2_LED": + { + "dev_info": { "device_type":"LED", "device_name":"FANTRAY_LED"}, + "dev_attr": { "index":"1"}, + "i2c" : { + "attr_list": + [ + {"attr_name":"off","attr_devtype":"cpld","attr_devname":"CPLD_BASEBOARD","bits":"1:0","descr":"Off","value":"0x03","swpld_addr":"0x0d","swpld_addr_offset":"0xb9"}, + {"attr_name":"green","attr_devtype":"cpld","attr_devname":"CPLD_BASEBOARD","bits":"1:0","descr":"Green","value":"0x02","swpld_addr":"0x0d","swpld_addr_offset":"0xb9"}, + {"attr_name":"amber","attr_devtype":"cpld","attr_devname":"CPLD_BASEBOARD","bits":"1:0","descr":"Amber","value":"0x01","swpld_addr":"0x0d","swpld_addr_offset":"0xb9"} + ] + } + }, + "FANTRAY3_LED": + { + "dev_info": { "device_type":"LED", "device_name":"FANTRAY_LED"}, + "dev_attr": { "index":"2"}, + "i2c" : { + "attr_list": + [ + {"attr_name":"off","attr_devtype":"cpld","attr_devname":"CPLD_BASEBOARD","bits":"1:0","descr":"Off","value":"0x03","swpld_addr":"0x0d","swpld_addr_offset":"0xbf"}, + {"attr_name":"green","attr_devtype":"cpld","attr_devname":"CPLD_BASEBOARD","bits":"1:0","descr":"Green","value":"0x02","swpld_addr":"0x0d","swpld_addr_offset":"0xbf"}, + {"attr_name":"amber","attr_devtype":"cpld","attr_devname":"CPLD_BASEBOARD","bits":"1:0","descr":"Amber","value":"0x01","swpld_addr":"0x0d","swpld_addr_offset":"0xbf"} + ] + } + }, + "FANTRAY4_LED": + { + "dev_info": { "device_type":"LED", "device_name":"FANTRAY_LED"}, + "dev_attr": { "index":"3"}, + "i2c" : { + "attr_list": + [ + {"attr_name":"off","attr_devtype":"cpld","attr_devname":"CPLD_BASEBOARD","bits":"1:0","descr":"Off","value":"0x03","swpld_addr":"0x0d","swpld_addr_offset":"0xc5"}, + {"attr_name":"green","attr_devtype":"cpld","attr_devname":"CPLD_BASEBOARD","bits":"1:0","descr":"Green","value":"0x02","swpld_addr":"0x0d","swpld_addr_offset":"0xc5"}, + {"attr_name":"amber","attr_devtype":"cpld","attr_devname":"CPLD_BASEBOARD","bits":"1:0","descr":"Amber","value":"0x01","swpld_addr":"0x0d","swpld_addr_offset":"0xc5"} + ] + } + }, + + "PSU_LED": + { + "dev_info": { "device_type":"LED", "device_name":"PSU_LED"}, + "dev_attr": { "index":"0", "flag": "rw"}, + "i2c" : { + "attr_list": + [ + {"attr_name":"green", "descr": "Green", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "bits" : "7:6", "value" : "0x1", "swpld_addr" : "0x0d", "swpld_addr_offset" : "0x61"}, + {"attr_name":"amber", "descr": "Amber", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "bits" : "7:6", "value" : "0x2", "swpld_addr" : "0x0d", "swpld_addr_offset" : "0x61"}, + {"attr_name":"off", "descr": "Off", "attr_devtype":"cpld", "attr_devname":"CPLD_BASEBOARD", "bits" : "7:6", "value" : "0x3", "swpld_addr" : "0x0d", "swpld_addr_offset" : "0x61"} + ] + } + } +} diff --git a/device/celestica/x86_64-cel_ds3000-r0/pddf_support b/device/celestica/x86_64-cel_ds3000-r0/pddf_support new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/device/celestica/x86_64-cel_ds3000-r0/platform.json b/device/celestica/x86_64-cel_ds3000-r0/platform.json new file mode 100644 index 000000000000..2fd40e3b05a5 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/platform.json @@ -0,0 +1,559 @@ +{ + "chassis": { + "name": "DS3000", + "status_led": { + "controllable": false + }, + "fans": [ + { + "name": "Fantray1_1", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + }, + { + "name": "Fantray1_2", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + }, + { + "name": "Fantray2_1", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + }, + { + "name": "Fantray2_2", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + }, + { + "name": "Fantray3_1", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + }, + { + "name": "Fantray3_2", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + }, + { + "name": "Fantray4_1", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + }, + { + "name": "Fantray4_2", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + } + ], + "fan_drawers": [ + { + "name": "Fantray1", + "status_led": { + "controllable": true, + "colors": ["red", "green", "amber", "off"] + }, + "fans": [ + { + "name": "Fantray1_1", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + }, + { + "name": "Fantray1_2", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + } + ] + }, + { + "name": "Fantray2", + "status_led": { + "controllable": true, + "colors": ["red", "green", "amber", "off"] + }, + "fans": [ + { + "name": "Fantray2_1", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + }, + { + "name": "Fantray2_2", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + } + ] + }, + { + "name": "Fantray3", + "status_led": { + "controllable": true, + "colors": ["red", "green", "amber", "off"] + }, + "fans": [ + { + "name": "Fantray3_1", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + }, + { + "name": "Fantray3_2", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + } + ] + }, + { + "name": "Fantray4", + "status_led": { + "controllable": true, + "colors": ["red", "green", "amber", "off"] + }, + "fans": [ + { + "name": "Fantray4_1", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + }, + { + "name": "Fantray4_2", + "status_led": { + "controllable": false + }, + "speed": { + "controllable": false + } + } + ] + } + ], + "psus": [ + { + "name": "PSU 1", + "status_led": { + "controllable": true, + "colors": ["green", "off"] + }, + "fans": [ + { + "name": "PSU1_FAN1", + "speed": { + "controllable": false + }, + "status_led": { + "available": false + } + } + ] + }, + { + "name": "PSU 2", + "status_led": { + "controllable": true, + "colors": ["green", "off"] + }, + "fans": [ + { + "name": "PSU2_FAN1", + "speed": { + "controllable": false + }, + "status_led": { + "available": false + } + } + ] + } + ] + }, + "interfaces": { + "Ethernet0": { + "index": "1,1,1,1", + "lanes": "1,2,3,4", + "breakout_modes": { + "1x100G": ["Eth1/1/1"], + "1x40G": ["Eth1/1/1"], + "4x25G": ["Eth1/1/1", "Eth1/1/2", "Eth1/1/3", "Eth1/1/4"], + "4x10G": ["Eth1/1/1", "Eth1/1/2", "Eth1/1/3", "Eth1/1/4"] + } + }, + "Ethernet4": { + "index": "2,2,2,2", + "lanes": "5,6,7,8", + "breakout_modes": { + "1x100G": ["Eth1/2/1"], + "1x40G": ["Eth1/2/1"], + "4x25G": ["Eth1/2/1", "Eth1/2/2", "Eth1/2/3", "Eth1/2/4"], + "4x10G": ["Eth1/2/1", "Eth1/2/2", "Eth1/2/3", "Eth1/2/4"] + } + }, + "Ethernet8": { + "index": "3,3,3,3", + "lanes": "9,10,11,12", + "breakout_modes": { + "1x100G": ["Eth1/3/1"], + "1x40G": ["Eth1/3/1"], + "4x25G": ["Eth1/3/1", "Eth1/3/2", "Eth1/3/3", "Eth1/3/4"], + "4x10G": ["Eth1/3/1", "Eth1/3/2", "Eth1/3/3", "Eth1/3/4"] + } + }, + "Ethernet12": { + "index": "4,4,4,4", + "lanes": "13,14,15,16", + "breakout_modes": { + "1x100G": ["Eth1/4/1"], + "1x40G": ["Eth1/4/1"], + "4x25G": ["Eth1/4/1", "Eth1/4/2", "Eth1/4/3", "Eth1/4/4"], + "4x10G": ["Eth1/4/1", "Eth1/4/2", "Eth1/4/3", "Eth1/4/4"] + } + }, + "Ethernet16": { + "index": "5,5,5,5", + "lanes": "17,18,19,20", + "breakout_modes": { + "1x100G": ["Eth1/5/1"], + "1x40G": ["Eth1/5/1"], + "4x25G": ["Eth1/5/1", "Eth1/5/2", "Eth1/5/3", "Eth1/5/4"], + "4x10G": ["Eth1/5/1", "Eth1/5/2", "Eth1/5/3", "Eth1/5/4"] + } + }, + "Ethernet20": { + "index": "6,6,6,6", + "lanes": "21,22,23,24", + "breakout_modes": { + "1x100G": ["Eth1/6/1"], + "1x40G": ["Eth1/6/1"], + "4x25G": ["Eth1/6/1", "Eth1/6/2", "Eth1/6/3", "Eth1/6/4"], + "4x10G": ["Eth1/6/1", "Eth1/6/2", "Eth1/6/3", "Eth1/6/4"] + } + }, + "Ethernet24": { + "index": "7,7,7,7", + "lanes": "25,26,27,28", + "breakout_modes": { + "1x100G": ["Eth1/7/1"], + "1x40G": ["Eth1/7/1"], + "4x25G": ["Eth1/7/1", "Eth1/7/2", "Eth1/7/3", "Eth1/7/4"], + "4x10G": ["Eth1/7/1", "Eth1/7/2", "Eth1/7/3", "Eth1/7/4"] + } + }, + "Ethernet28": { + "index": "8,8,8,8", + "lanes": "29,30,31,32", + "breakout_modes": { + "1x100G": ["Eth1/8/1"], + "1x40G": ["Eth1/8/1"], + "4x25G": ["Eth1/8/1", "Eth1/8/2", "Eth1/8/3", "Eth1/8/4"], + "4x10G": ["Eth1/8/1", "Eth1/8/2", "Eth1/8/3", "Eth1/8/4"] + } + }, + "Ethernet32": { + "index": "9,9,9,9", + "lanes": "33,34,35,36", + "breakout_modes": { + "1x100G": ["Eth1/9/1"], + "1x40G": ["Eth1/9/1"], + "4x25G": ["Eth1/9/1", "Eth1/9/2", "Eth1/9/3", "Eth1/9/4"], + "4x10G": ["Eth1/9/1", "Eth1/9/2", "Eth1/9/3", "Eth1/9/4"] + } + }, + "Ethernet36": { + "index": "10,10,10,10", + "lanes": "37,38,39,40", + "breakout_modes": { + "1x100G": ["Eth1/10/1"], + "1x40G": ["Eth1/10/1"], + "4x25G": ["Eth1/10/1", "Eth1/10/2", "Eth1/10/3", "Eth1/10/4"], + "4x10G": ["Eth1/10/1", "Eth1/10/2", "Eth1/10/3", "Eth1/10/4"] + } + }, + "Ethernet40": { + "index": "11,11,11,11", + "lanes": "41,42,43,44", + "breakout_modes": { + "1x100G": ["Eth1/11/1"], + "1x40G": ["Eth1/11/1"], + "4x25G": ["Eth1/11/1", "Eth1/11/2", "Eth1/11/3", "Eth1/11/4"], + "4x10G": ["Eth1/11/1", "Eth1/11/2", "Eth1/11/3", "Eth1/11/4"] + } + }, + "Ethernet44": { + "index": "12,12,12,12", + "lanes": "45,46,47,48", + "breakout_modes": { + "1x100G": ["Eth1/12/1"], + "1x40G": ["Eth1/12/1"], + "4x25G": ["Eth1/12/1", "Eth1/12/2", "Eth1/12/3", "Eth1/12/4"], + "4x10G": ["Eth1/12/1", "Eth1/12/2", "Eth1/12/3", "Eth1/12/4"] + } + }, + "Ethernet48": { + "index": "13,13,13,13", + "lanes": "49,50,51,52", + "breakout_modes": { + "1x100G": ["Eth1/13/1"], + "1x40G": ["Eth1/13/1"], + "4x25G": ["Eth1/13/1", "Eth1/13/2", "Eth1/13/3", "Eth1/13/4"], + "4x10G": ["Eth1/13/1", "Eth1/13/2", "Eth1/13/3", "Eth1/13/4"] + } + }, + "Ethernet52": { + "index": "14,14,14,14", + "lanes": "53,54,55,56", + "breakout_modes": { + "1x100G": ["Eth1/14/1"], + "1x40G": ["Eth1/14/1"], + "4x25G": ["Eth1/14/1", "Eth1/14/2", "Eth1/14/3", "Eth1/14/4"], + "4x10G": ["Eth1/14/1", "Eth1/14/2", "Eth1/14/3", "Eth1/14/4"] + } + }, + "Ethernet56": { + "index": "15,15,15,15", + "lanes": "57,58,59,60", + "breakout_modes": { + "1x100G": ["Eth1/15/1"], + "1x40G": ["Eth1/15/1"], + "4x25G": ["Eth1/15/1", "Eth1/15/2", "Eth1/15/3", "Eth1/15/4"], + "4x10G": ["Eth1/15/1", "Eth1/15/2", "Eth1/15/3", "Eth1/15/4"] + } + }, + "Ethernet60": { + "index": "16,16,16,16", + "lanes": "61,62,63,64", + "breakout_modes": { + "1x100G": ["Eth1/16/1"], + "1x40G": ["Eth1/16/1"], + "4x25G": ["Eth1/16/1", "Eth1/16/2", "Eth1/16/3", "Eth1/16/4"], + "4x10G": ["Eth1/16/1", "Eth1/16/2", "Eth1/16/3", "Eth1/16/4"] + } + }, + "Ethernet64": { + "index": "17,17,17,17", + "lanes": "65,66,67,68", + "breakout_modes": { + "1x100G": ["Eth1/17/1"], + "1x40G": ["Eth1/17/1"], + "4x25G": ["Eth1/17/1", "Eth1/17/2", "Eth1/17/3", "Eth1/17/4"], + "4x10G": ["Eth1/17/1", "Eth1/17/2", "Eth1/17/3", "Eth1/17/4"] + } + }, + "Ethernet68": { + "index": "18,18,18,18", + "lanes": "69,70,71,72", + "breakout_modes": { + "1x100G": ["Eth1/18/1"], + "1x40G": ["Eth1/18/1"], + "4x25G": ["Eth1/18/1", "Eth1/18/2", "Eth1/18/3", "Eth1/18/4"], + "4x10G": ["Eth1/18/1", "Eth1/18/2", "Eth1/18/3", "Eth1/18/4"] + } + }, + "Ethernet72": { + "index": "19,19,19,19", + "lanes": "73,74,75,76", + "breakout_modes": { + "1x100G": ["Eth1/19/1"], + "1x40G": ["Eth1/19/1"], + "4x25G": ["Eth1/19/1", "Eth1/19/2", "Eth1/19/3", "Eth1/19/4"], + "4x10G": ["Eth1/19/1", "Eth1/19/2", "Eth1/19/3", "Eth1/19/4"] + } + }, + "Ethernet76": { + "index": "20,20,20,20", + "lanes": "77,78,79,80", + "breakout_modes": { + "1x100G": ["Eth1/20/1"], + "1x40G": ["Eth1/20/1"], + "4x25G": ["Eth1/20/1", "Eth1/20/2", "Eth1/20/3", "Eth1/20/4"], + "4x10G": ["Eth1/20/1", "Eth1/20/2", "Eth1/20/3", "Eth1/20/4"] + } + }, + "Ethernet80": { + "index": "21,21,21,21", + "lanes": "81,82,83,84", + "breakout_modes": { + "1x100G": ["Eth1/21/1"], + "1x40G": ["Eth1/21/1"], + "4x25G": ["Eth1/21/1", "Eth1/21/2", "Eth1/21/3", "Eth1/21/4"], + "4x10G": ["Eth1/21/1", "Eth1/21/2", "Eth1/21/3", "Eth1/21/4"] + } + }, + "Ethernet84": { + "index": "22,22,22,22", + "lanes": "85,86,87,88", + "breakout_modes": { + "1x100G": ["Eth1/22/1"], + "1x40G": ["Eth1/22/1"], + "4x25G": ["Eth1/22/1", "Eth1/22/2", "Eth1/22/3", "Eth1/22/4"], + "4x10G": ["Eth1/22/1", "Eth1/22/2", "Eth1/22/3", "Eth1/22/4"] + } + }, + "Ethernet88": { + "index": "23,23,23,23", + "lanes": "89,90,91,92", + "breakout_modes": { + "1x100G": ["Eth1/23/1"], + "1x40G": ["Eth1/23/1"], + "4x25G": ["Eth1/23/1", "Eth1/23/2", "Eth1/23/3", "Eth1/23/4"], + "4x10G": ["Eth1/23/1", "Eth1/23/2", "Eth1/23/3", "Eth1/23/4"] + } + }, + "Ethernet92": { + "index": "24,24,24,24", + "lanes": "93,94,95,96", + "breakout_modes": { + "1x100G": ["Eth1/24/1"], + "1x40G": ["Eth1/24/1"], + "4x25G": ["Eth1/24/1", "Eth1/24/2", "Eth1/24/3", "Eth1/24/4"], + "4x10G": ["Eth1/24/1", "Eth1/24/2", "Eth1/24/3", "Eth1/24/4"] + } + }, + "Ethernet96": { + "index": "25,25,25,25", + "lanes": "97,98,99,100", + "breakout_modes": { + "1x100G": ["Eth1/25/1"], + "1x40G": ["Eth1/25/1"], + "4x25G": ["Eth1/25/1", "Eth1/25/2", "Eth1/25/3", "Eth1/25/4"], + "4x10G": ["Eth1/25/1", "Eth1/25/2", "Eth1/25/3", "Eth1/25/4"] + } + }, + "Ethernet100": { + "index": "26,26,26,26", + "lanes": "101,102,103,104", + "breakout_modes": { + "1x100G": ["Eth1/26/1"], + "1x40G": ["Eth1/26/1"], + "4x25G": ["Eth1/26/1", "Eth1/26/2", "Eth1/26/3", "Eth1/26/4"], + "4x10G": ["Eth1/26/1", "Eth1/26/2", "Eth1/26/3", "Eth1/26/4"] + } + }, + "Ethernet104": { + "index": "27,27,27,27", + "lanes": "105,106,107,108", + "breakout_modes": { + "1x100G": ["Eth1/27/1"], + "1x40G": ["Eth1/27/1"], + "4x25G": ["Eth1/27/1", "Eth1/27/2", "Eth1/27/3", "Eth1/27/4"], + "4x10G": ["Eth1/27/1", "Eth1/27/2", "Eth1/27/3", "Eth1/27/4"] + } + }, + "Ethernet108": { + "index": "28,28,28,28", + "lanes": "109,110,111,112", + "breakout_modes": { + "1x100G": ["Eth1/28/1"], + "1x40G": ["Eth1/28/1"], + "4x25G": ["Eth1/28/1", "Eth1/28/2", "Eth1/28/3", "Eth1/28/4"], + "4x10G": ["Eth1/28/1", "Eth1/28/2", "Eth1/28/3", "Eth1/28/4"] + } + }, + "Ethernet112": { + "index": "29,29,29,29", + "lanes": "113,114,115,116", + "breakout_modes": { + "1x100G": ["Eth1/29/1"], + "1x40G": ["Eth1/29/1"], + "4x25G": ["Eth1/29/1", "Eth1/29/2", "Eth1/29/3", "Eth1/29/4"], + "4x10G": ["Eth1/29/1", "Eth1/29/2", "Eth1/29/3", "Eth1/29/4"] + } + }, + "Ethernet116": { + "index": "30,30,30,30", + "lanes": "117,118,119,120", + "breakout_modes": { + "1x100G": ["Eth1/30/1"], + "1x40G": ["Eth1/30/1"], + "4x25G": ["Eth1/30/1", "Eth1/30/2", "Eth1/30/3", "Eth1/30/4"], + "4x10G": ["Eth1/30/1", "Eth1/30/2", "Eth1/30/3", "Eth1/30/4"] + } + }, + "Ethernet120": { + "index": "31,31,31,31", + "lanes": "121,122,123,124", + "breakout_modes": { + "1x100G": ["Eth1/31/1"], + "1x40G": ["Eth1/31/1"], + "4x25G": ["Eth1/31/1", "Eth1/31/2", "Eth1/31/3", "Eth1/31/4"], + "4x10G": ["Eth1/31/1", "Eth1/31/2", "Eth1/31/3", "Eth1/31/4"] + } + }, + "Ethernet124": { + "index": "32,32,32,32", + "lanes": "125,126,127,128", + "breakout_modes": { + "1x100G": ["Eth1/32/1"], + "1x40G": ["Eth1/32/1"], + "4x25G": ["Eth1/32/1", "Eth1/32/2", "Eth1/32/3", "Eth1/32/4"], + "4x10G": ["Eth1/32/1", "Eth1/32/2", "Eth1/32/3", "Eth1/32/4"] + } + }, + "Ethernet128": { + "index": "33", + "lanes": "129", + "breakout_modes": { + "1x10G": ["Eth1/33"] + } + } + } +} diff --git a/device/celestica/x86_64-cel_ds3000-r0/platform_asic b/device/celestica/x86_64-cel_ds3000-r0/platform_asic new file mode 100644 index 000000000000..960467652765 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/platform_asic @@ -0,0 +1 @@ +broadcom diff --git a/device/celestica/x86_64-cel_ds3000-r0/platform_components.json b/device/celestica/x86_64-cel_ds3000-r0/platform_components.json new file mode 100644 index 000000000000..614bf3b1d315 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/platform_components.json @@ -0,0 +1,17 @@ +{ + "chassis": { + "DS3000": { + "component": { + "BIOS": {}, + "BMC": {}, + "BaseBoard_CPLD": {}, + "SwitchBoard_CPLD1": {}, + "SwitchBoard_CPLD2": {}, + "COMeBoard_CPLD": {}, + "FPGA": {}, + "PCIe": {}, + "SSD": {} + } + } + } +} diff --git a/device/celestica/x86_64-cel_ds3000-r0/pmon_daemon_control.json b/device/celestica/x86_64-cel_ds3000-r0/pmon_daemon_control.json new file mode 100644 index 000000000000..5e59513ef696 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/pmon_daemon_control.json @@ -0,0 +1,7 @@ +{ + "skip_ledd": true, + "skip_xcvrd": false, + "skip_psud": false, + "skip_syseepromd": false, + "skip_fancontrol": true +} \ No newline at end of file diff --git a/device/celestica/x86_64-cel_ds3000-r0/sensors.conf b/device/celestica/x86_64-cel_ds3000-r0/sensors.conf new file mode 100644 index 000000000000..8d441077a677 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/sensors.conf @@ -0,0 +1,87 @@ +# LM75B temperature sensors +bus "i2c-109" "i2c-pci-9" + chip "lm75-i2c-109-4a" + label temp1 "Switchboard U28 Sensor Temp" + set temp1_max 60 + set temp1_max_hyst 57 + chip "lm75-i2c-109-4b" + label temp1 "Switchboard U29 Sensor Temp" + set temp1_max 60 + set temp1_max_hyst 57 + chip "lm75-i2c-109-4c" + label temp1 "Switchboard U17 Sensor Temp" + chip "lm75-i2c-109-49" + label temp1 "Switchboard U18 Sensor Temp" + chip "lm75-i2c-109-4d" + label temp1 "Baseboard U5 Sensor Temp" + set temp1_max 55 + set temp1_max_hyst 52 + chip "lm75-i2c-109-4e" + label temp1 "Baseboard U56 Sensor Temp" + set temp1_max 55 + set temp1_max_hyst 52 + +# PSU +bus "i2c-43" "i2c-106-mux (chan_id 1)" + chip "psu_pmbus-i2c-43-5b" + label in3 "PSU2 input voltage" + label fan1 "PSU2 FAN speed" + label temp1 "PSU2 temperature" + label power2 "PSU2 AC Power Voltage" + label curr2 "PSU2 AC current" + +bus "i2c-42" "i2c-106-mux (chan_id 0)" + chip "psu_pmbus-i2c-42-5a" + label in3 "PSU1 input voltage" + label fan1 "PSU1 FAN speed" + label temp1 "PSU1 temperature" + label power2 "PSU1 AC Power Voltage" + label curr2 "PSU1 AC current" + +# MP2975 power chip +bus "i2c-108" "i2c-pci-8" + chip "mp2975-i2c-108-70" + label in1 "VDD ANLG input voltage" + label in2 "VDD ANLG output voltage" + label in3 "VDD ANLG output voltage" + label temp1 "VDD ANLG temperature" + label power1 "VDD ANLG input power" + label power2 "VDD ANLG output power" + label power3 "VDD ANLG output power" + label curr1 "VDD ANLG input current" + label curr2 "VDD ANLG output current" + label curr3 "VDD ANLG output current" + label curr4 "VDD ANLG output current" + label curr5 "VDD ANLG output current" + +bus "i2c-108" "i2c-pci-8" + chip "mp2975-i2c-108-7a" + label in1 "VDD CORE input voltage" + label in2 "VDD CORE output voltage" + label in3 "VDD CORE output voltage" + label temp1 "VDD CORE temperature" + label power1 "VDD CORE input power" + label power2 "VDD CORE output power" + label power3 "VDD CORE output power" + label curr1 "VDD CORE input current" + label curr2 "VDD CORE output current" + label curr3 "VDD CORE output current" + label curr4 "VDD CORE output current" + label curr5 "VDD CORE output current" + label curr6 "VDD CORE output current" + label curr7 "VDD CORE output current" + label curr8 "VDD CORE output current" + label curr9 "VDD CORE output current" + label curr10 "VDD CORE output current" + +# CPLD FAN +bus "i2c-105" "i2c-pci-5" + chip "fan_cpld-i2c-105-16" + label fan1 "FAN1 Front Fan" + label fan2 "FAN1 Rear Fan" + label fan3 "FAN2 Front Fan" + label fan4 "FAN2 Rear Fan" + label fan5 "FAN3 Front Fan" + label fan6 "FAN3 Rear Fan" + label fan7 "FAN4 Front Fan" + label fan8 "FAN4 Rear Fan" diff --git a/device/celestica/x86_64-cel_ds3000-r0/system_health_monitoring_config.json b/device/celestica/x86_64-cel_ds3000-r0/system_health_monitoring_config.json new file mode 100644 index 000000000000..42152ac3dc54 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/system_health_monitoring_config.json @@ -0,0 +1,14 @@ +{ + "services_to_ignore": [], + "devices_to_ignore": [ + "psu.temperature", + "psu.voltage" + ], + "user_defined_checkers": [], + "polling_interval": 60, + "led_color": { + "fault": "amber", + "normal": "green", + "booting": "amber_blink" + } +} diff --git a/device/celestica/x86_64-cel_ds3000-r0/thermal_policy.json b/device/celestica/x86_64-cel_ds3000-r0/thermal_policy.json new file mode 100644 index 000000000000..515348ec6ab0 --- /dev/null +++ b/device/celestica/x86_64-cel_ds3000-r0/thermal_policy.json @@ -0,0 +1,136 @@ +{ + "interval": 4, + "thermal_control_algorithm": { + "run_at_boot_up": "True", + "fan_speed_when_suspend": "50" + }, + "info_types": [ + { + "type": "fan_info" + }, + { + "type": "psu_info" + }, + { + "type": "thermal_info" + }, + { + "type": "chassis_info" + } + ], + "policies": [ + { + "name": "temp over high critical threshold", + "conditions": [ + { + "type": "thermal.over.high_critical_threshold" + } + ], + "actions": [ + { + "type": "switch.shutdown" + } + ] + }, + { + "name": "any fantray absence", + "conditions": [ + { + "type": "fantray.any.absence" + } + ], + "actions": [ + { + "type": "fan.all.set_speed", + "speed": "100" + } + ] + }, + { + "name": "more than one fan rotor failed", + "conditions": [ + { + "type": "fan.rotor.more_than_one.failed" + } + ], + "actions": [ + { + "type": "fan.all.set_speed", + "speed": "100" + } + ] + }, + { + "name": "any psu absence", + "conditions": [ + { + "type": "psu.any.absence" + } + ], + "actions": [ + { + "type": "fan.all.set_speed", + "speed": "100" + } + ] + }, + { + "name": "any thermal over high threshold", + "conditions": [ + { + "type": "thermal.any.over.high_threshold" + } + ], + "actions": [ + { + "type": "fan.all.set_speed", + "speed": "100" + } + ] + }, + { + "name": "any thermal below high threshold", + "conditions": [ + { + "type": "thermal.any.below.low_threshold" + } + ], + "actions": [ + { + "type": "fan.all.set_speed", + "speed": "10" + } + ] + }, + { + "name": "thermal control algorithm", + "conditions": [ + { + "type": "fantray.all.presence" + }, + { + "type": "fan.rotor.less_than_two.failed" + }, + { + "type": "psu.all.presence" + }, + { + "type": "thermal.all.below.high_threshold" + }, + { + "type": "thermal.all.over.low_threshold" + } + ], + "actions": [ + { + "thermal_log_level": 5, + "type": "thermal.temp_check_and_fsc_algo_control", + "cpu_pid_params": [78, 3, 0.5, 0.2], + "bcm_pid_params": [88, 4, 0.3, 0.4], + "f2b_linear_params": [34, 54, 3, 35, 100], + "b2f_linear_params": [27, 48, 3, 35, 100] + } + ] + } + ] +} diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-ds3000.install b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-ds3000.install new file mode 100644 index 000000000000..88c82ba4fb92 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-ds3000.install @@ -0,0 +1,8 @@ +ds3000/systemd/pddf-platform-init.service etc/systemd/system +ds3000/pddf/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/x86_64-cel_ds3000-r0/pddf +ds3000/scripts/pre_pddf_init.sh usr/local/bin +ds3000/scripts/pddf_pre_driver_install.sh usr/local/bin +ds3000/scripts/pddf_post_device_create.sh usr/local/bin +ds3000/utils/afulnx_64 usr/local/bin +ds3000/utils/fpga_prog usr/local/bin +ds3000/utils/ispvm usr/local/bin diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-ds3000.postinst b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-ds3000.postinst new file mode 100644 index 000000000000..f8b021b6248a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-ds3000.postinst @@ -0,0 +1,4 @@ +depmod -a + +systemctl enable pddf-platform-init.service +systemctl start pddf-platform-init.service diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/Makefile b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/Makefile new file mode 100644 index 000000000000..031e2163cf52 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/Makefile @@ -0,0 +1,8 @@ +LED_TARGET:= pddf_custom_led_module +$(LED_TARGET)-objs := ./led_driver/pddf_custom_led_module.o + +PSU_TARGET:= pddf_custom_psu_driver_module +$(PSU_TARGET)-objs := ./psu_driver/pddf_psu_api.o ./psu_driver/pddf_psu_driver.o + +obj-m := lm75.o mc24lc64t.o baseboard_cpld.o pddf_custom_fpga_algo.o pddf_custom_fpga_extend.o +obj-m += $(LED_TARGET).o $(PSU_TARGET).o diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/baseboard_cpld.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/baseboard_cpld.c new file mode 100644 index 000000000000..dd5a8c4d267e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/baseboard_cpld.c @@ -0,0 +1,414 @@ +/* + * baseboard_cpld.c - driver for DS3000 Base Board CPLD + * This driver implement sysfs for CPLD register access using LPC bus. + * Copyright (C) 2019 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "baseboard" +/** + * CPLD register address for read and write. + */ +#define VERSION_ADDR 0xA100 +#define SCRATCH_ADDR 0xA101 +#define SYS_LED_ADDR 0xA162 +#define CARD_PRES_ADDR 0xA108 +#define COME_CPLD_VER_ADDR 0xA1E0 + +#define CPLD_REGISTER_SIZE 0x77 + +struct baseboard_cpld_data { + struct mutex cpld_lock; + uint16_t read_addr; +}; + +struct baseboard_cpld_data *cpld_data; + +/** + * Read the value from scratch register as hex string. + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return Hex string read from scratch register. + */ +static ssize_t scratch_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + mutex_lock(&cpld_data->cpld_lock); + data = inb(SCRATCH_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return sprintf(buf,"0x%2.2x\n", data); +} + +/** + * Set scratch register with specific hex string. + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t scratch_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned long data; + char *last; + + mutex_lock(&cpld_data->cpld_lock); + data = (uint16_t)strtoul(buf,&last,16); + if(data == 0 && buf == last){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + outb(data, SCRATCH_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_RW(scratch); + + +/* CPLD version attributes */ +static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int len = -EIO; + // CPLD register is one byte + mutex_lock(&cpld_data->cpld_lock); + len = sprintf(buf, "0x%2.2x\n",inb(VERSION_ADDR)); + mutex_unlock(&cpld_data->cpld_lock); + return len; +} +static DEVICE_ATTR_RO(version); + + +static ssize_t getreg_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + // CPLD register is one byte + uint16_t addr; + char *last; + + addr = (uint16_t)strtoul(buf,&last,16); + if(addr == 0 && buf == last){ + return -EINVAL; + } + cpld_data->read_addr = addr; + return count; +} + +static ssize_t getreg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int len = -EIO; + // CPLD register is one byte + mutex_lock(&cpld_data->cpld_lock); + len = sprintf(buf, "0x%2.2x\n",inb(cpld_data->read_addr)); + mutex_unlock(&cpld_data->cpld_lock); + return len; +} +static DEVICE_ATTR_RW(getreg); + +static ssize_t setreg_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + // CPLD register is one byte + uint16_t addr; + uint8_t value; + char *tok; + char clone[count]; + char *pclone = clone; + char *last; + + strcpy(clone, buf); + + mutex_lock(&cpld_data->cpld_lock); + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + addr = (uint16_t)strtoul(tok,&last,16); + if(addr == 0 && tok == last){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + + tok = strsep((char**)&pclone, " "); + if(tok == NULL){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + value = (uint8_t)strtoul(tok,&last,16); + if(value == 0 && tok == last){ + mutex_unlock(&cpld_data->cpld_lock); + return -EINVAL; + } + + outb(value,addr); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_WO(setreg); + +/** + * Show system led status - on/off/1hz/4hz + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return Hex string read from scratch register. + */ +static ssize_t sys_led_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + data = data & 0x3; + return sprintf(buf, "%s\n", + data == 0x03 ? "off" : data == 0x02 ? "4hz" : data ==0x01 ? "1hz": "on"); +} + +/** + * Set the status of system led - on/off/1hz/4hz + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t sys_led_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned char led_status,data; + if(sysfs_streq(buf, "off")){ + led_status = 0x03; + }else if(sysfs_streq(buf, "4hz")){ + led_status = 0x02; + }else if(sysfs_streq(buf, "1hz")){ + led_status = 0x01; + }else if(sysfs_streq(buf, "on")){ + led_status = 0x00; + }else{ + count = -EINVAL; + return count; + } + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + data = data & ~(0x3); + data = data | led_status; + outb(data, SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_RW(sys_led); + +/** + * Show system led color - both/green/yellow/none + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return Hex string read from scratch register. + */ +static ssize_t sys_led_color_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + data = (data >> 4) & 0x3; + return sprintf(buf, "%s\n", + data == 0x03 ? "off" : data == 0x02 ? "yellow" : data ==0x01 ? "green": "both"); +} + +/** + * Set the color of system led - both/green/yellow/none + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer of set value + * @param count number of bytes in buffer + * @return number of bytes written, or error code < 0. + */ +static ssize_t sys_led_color_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned char led_status,data; + if(sysfs_streq(buf, "off")){ + led_status = 0x03; + }else if(sysfs_streq(buf, "yellow")){ + led_status = 0x02; + }else if(sysfs_streq(buf, "green")){ + led_status = 0x01; + }else if(sysfs_streq(buf, "both")){ + led_status = 0x00; + }else{ + count = -EINVAL; + return count; + } + mutex_lock(&cpld_data->cpld_lock); + data = inb(SYS_LED_ADDR); + data = data & ~( 0x3 << 4); + data = data | (led_status << 4); + outb(data, SYS_LED_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + return count; +} +static DEVICE_ATTR_RW(sys_led_color); + +/** + * Show BMC card presence + * @param dev kernel device + * @param devattr kernel device attribute + * @param buf buffer for get value + * @return string absent or present of BMC card. + */ +static ssize_t bmc_presence_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + unsigned char data = 0; + mutex_lock(&cpld_data->cpld_lock); + data = inb(CARD_PRES_ADDR); + mutex_unlock(&cpld_data->cpld_lock); + data = data & 0x1; + return sprintf(buf, "%s\n", + data == 0x01 ? "absent" : "present"); +} +static DEVICE_ATTR_RO(bmc_presence); + +/* COME CPLD version attributes */ +static ssize_t come_cpld_version_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int len = -EIO; + // COME CPLD register is one byte + mutex_lock(&cpld_data->cpld_lock); + len = sprintf(buf, "0x%2.2x\n",inb(COME_CPLD_VER_ADDR)); + mutex_unlock(&cpld_data->cpld_lock); + return len; +} +static DEVICE_ATTR_RO(come_cpld_version); + +static struct attribute *baseboard_cpld_attrs[] = { + &dev_attr_version.attr, + &dev_attr_scratch.attr, + &dev_attr_getreg.attr, + &dev_attr_setreg.attr, + &dev_attr_sys_led.attr, + &dev_attr_sys_led_color.attr, + &dev_attr_bmc_presence.attr, + &dev_attr_come_cpld_version.attr, + NULL, +}; + +static struct attribute_group baseboard_cpld_attrs_grp = { + .attrs = baseboard_cpld_attrs, +}; + +static struct resource baseboard_cpld_resources[] = { + { + .start = 0xA100, + .end = 0xA1FF, + .flags = IORESOURCE_IO, + }, +}; + +static void baseboard_cpld_dev_release( struct device * dev) +{ + return; +} + +static struct platform_device baseboard_cpld_dev = { + .name = DRIVER_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(baseboard_cpld_resources), + .resource = baseboard_cpld_resources, + .dev = { + .release = baseboard_cpld_dev_release, + } +}; + +static int baseboard_cpld_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret =0; + + cpld_data = devm_kzalloc(&pdev->dev, sizeof(struct baseboard_cpld_data), + GFP_KERNEL); + if (!cpld_data) + return -ENOMEM; + + mutex_init(&cpld_data->cpld_lock); + + cpld_data->read_addr = VERSION_ADDR; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (unlikely(!res)) { + printk(KERN_ERR "Specified Resource Not Available...\n"); + return -1; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &baseboard_cpld_attrs_grp); + if (ret) { + printk(KERN_ERR "Cannot create sysfs for baseboard CPLD\n"); + } + return 0; +} + +static int baseboard_cpld_drv_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &baseboard_cpld_attrs_grp); + return 0; +} + +static struct platform_driver baseboard_cpld_drv = { + .probe = baseboard_cpld_drv_probe, + .remove = __exit_p(baseboard_cpld_drv_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +int baseboard_cpld_init(void) +{ + // Register platform device and platform driver + platform_device_register(&baseboard_cpld_dev); + platform_driver_register(&baseboard_cpld_drv); + return 0; +} + +void baseboard_cpld_exit(void) +{ + // Unregister platform device and platform driver + platform_driver_unregister(&baseboard_cpld_drv); + platform_device_unregister(&baseboard_cpld_dev); +} + +module_init(baseboard_cpld_init); +module_exit(baseboard_cpld_exit); + +MODULE_AUTHOR("Pradchaya Phucharoen "); +MODULE_DESCRIPTION("Celestica DS3000 Baseboard CPLD Driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/led_driver/pddf_custom_led_defs.h b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/led_driver/pddf_custom_led_defs.h new file mode 100644 index 000000000000..63628f708312 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/led_driver/pddf_custom_led_defs.h @@ -0,0 +1,149 @@ +/* + * Copyright 2019 Broadcom. + * The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + * 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. + * + * + * Description + * Platform LED related defines and structures + */ + + +/***************************************** + * kobj list + *****************************************/ + +struct kobject *platform_kobj=NULL; +struct kobject *led_kobj=NULL; + +struct kobject *state_attr_kobj=NULL; +struct kobject *cur_state_kobj=NULL; + +/***************************************** + * Static Data provided from user + * space JSON data file + *****************************************/ +#define NAME_SIZE 32 +#define VALUE_SIZE 5 +typedef enum { + STATUS_LED_COLOR_OFF=0, + STATUS_LED_COLOR_GREEN=1, + STATUS_LED_COLOR_YELLOW=2, + STATUS_LED_COLOR_RED=3, + STATUS_LED_COLOR_BLUE=4, + STATUS_LED_COLOR_GREEN_BLINK=5, + STATUS_LED_COLOR_YELLOW_BLINK=6, + STATUS_LED_COLOR_RED_BLINK=7, + STATUS_LED_COLOR_BLUE_BLINK=8, + STATUS_LED_COLOR_AMBER, + STATUS_LED_COLOR_AMBER_BLINK, + MAX_LED_STATUS +}LED_STATUS; + +char* LED_STATUS_STR[] = { + "off", + "green", + "yellow", + "red", + "blue", + "green_blink", + "yellow_blink", + "red_blink", + "blue_blink", + "amber", + "amber_blink" +}; + + +typedef struct +{ + char bits[NAME_SIZE]; + int pos; + int mask_bits; +}MASK_BITS; + +typedef struct +{ + int swpld_addr; + int swpld_addr_offset; + MASK_BITS bits; + u8 reg_values[VALUE_SIZE]; + char value[NAME_SIZE]; + char attr_devtype[NAME_SIZE]; + char attr_devname[NAME_SIZE]; +} LED_DATA; + +typedef struct +{ + int state; + char color[NAME_SIZE]; +/* S3IP System LED RW sysfs */ + int sys_led; + int bmc_led; + int fan_led; + int psu_led; + int loc_led; +/* S3IP Power LED RO sysfs */ + int psu1_led; + int psu2_led; +/* S3IP Fantray LED RO sysfs */ + int fantray1_led; + int fantray2_led; + int fantray3_led; + int fantray4_led; + int fantray5_led; + int fantray6_led; + int fantray7_led; +} CUR_STATE_DATA; + +typedef struct +{ + CUR_STATE_DATA cur_state; + char device_name[NAME_SIZE]; + int index; + LED_DATA data[MAX_LED_STATUS]; + int swpld_addr; + int swpld_addr_offset; + char attr_devtype[NAME_SIZE]; + char attr_devname[NAME_SIZE]; +} LED_OPS_DATA; + +typedef enum{ + LED_SYS, + LED_PSU, + LED_FAN, + LED_FANTRAY, + LED_DIAG, + LED_LOC, + LED_BMC, + LED_TYPE_MAX +} LED_TYPE; +char* LED_TYPE_STR[LED_TYPE_MAX] = +{ + "LED_SYS", + "LED_PSU", + "LED_FAN", + "LED_FANTRAY", + "LED_DIAG", + "LED_LOC", + "LED_BMC" +}; + +/***************************************** + * Data exported from kernel for + * user space plugin to get/set + *****************************************/ +#define PDDF_LED_DATA_ATTR( _prefix, _name, _mode, _show, _store, _type, _len, _addr) \ + struct pddf_data_attribute pddf_dev_##_prefix##_attr_##_name = { .dev_attr = __ATTR(_name, _mode, _show, _store), \ + .type = _type , \ + .len = _len , \ + .addr = _addr } diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/led_driver/pddf_custom_led_module.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/led_driver/pddf_custom_led_module.c new file mode 100644 index 000000000000..26541eaa6109 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/led_driver/pddf_custom_led_module.c @@ -0,0 +1,873 @@ +/* + * Copyright 2019 Broadcom. + * The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + * 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. + * + * + * A pddf kernel module to manage various LEDs of a switch + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include +#include +#include +#include "pddf_custom_led_defs.h" +#include "../../../../../pddf/i2c/modules/include/pddf_client_defs.h" +#include +#include +#include + +#define DEBUG 0 +#define MAX_PSU_NUM 2 +#define MAX_FANTRAY_NUM 7 +LED_OPS_DATA sys_led_ops_data[1]={0}; +LED_OPS_DATA psu_led_ops_data[MAX_PSU_NUM]={0}; +LED_OPS_DATA diag_led_ops_data[1]= {0}; +LED_OPS_DATA fan_led_ops_data[1]= {0}; +LED_OPS_DATA loc_led_ops_data[1]= {0}; +LED_OPS_DATA bmc_led_ops_data[1]= {0}; +LED_OPS_DATA fantray_led_ops_data[MAX_FANTRAY_NUM]={0}; +LED_OPS_DATA temp_data={0}; +LED_OPS_DATA* dev_list[LED_TYPE_MAX] = { + sys_led_ops_data, + psu_led_ops_data, + fan_led_ops_data, + fantray_led_ops_data, + diag_led_ops_data, + loc_led_ops_data, + bmc_led_ops_data, + NULL +}; +int num_psus = 0; +int num_fantrays = 0; + +extern int board_i2c_cpld_read_new(unsigned short cpld_addr, char *name, u8 reg); +extern int board_i2c_cpld_write_new(unsigned short cpld_addr, char *name, u8 reg, u8 value); +extern int board_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int board_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); +extern int board_i2c_fpga_read(unsigned short cpld_addr, u8 reg); +extern int board_i2c_fpga_write(unsigned short cpld_addr, u8 reg, u8 value); + +extern ssize_t show_pddf_data(struct device *dev, struct device_attribute *da, char *buf); +extern ssize_t store_pddf_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count); +extern ssize_t show_pddf_s3ip_data(struct device *dev, struct device_attribute *da, char *buf); +extern ssize_t store_pddf_s3ip_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count); + +static LED_STATUS find_state_index(const char* state_str) { + int index; + char *ptr = (char *)state_str; + while (*ptr && *ptr!= '\n' && *ptr !='\0') ptr++; + *ptr='\0'; + for ( index = 0; index < MAX_LED_STATUS; index++) { + if (strcmp(state_str, LED_STATUS_STR[index]) == 0 ) { + return index; + } + } + return MAX_LED_STATUS; +} + +static LED_TYPE get_dev_type(char* name) +{ + LED_TYPE ret = LED_TYPE_MAX; + if(strcasecmp(name, "SYS_LED") == 0) { + ret = LED_SYS; + } else if(strcasecmp(name, "FAN_LED") == 0) { + ret = LED_FAN; + } else if(strstr(name, "PSU_LED")) { + ret = LED_PSU; + } else if(strcasecmp(name, "DIAG_LED") == 0) { + ret = LED_DIAG; + } else if(strcasecmp(name, "LOC_LED") == 0) { + ret = LED_LOC; + } else if(strstr(name, "FANTRAY_LED")) { + ret = LED_FANTRAY; + } +#if DEBUG > 1 + pddf_dbg(LED, KERN_INFO "LED get_dev_type: %s; %d\n", name, ret); +#endif + return (ret); +} +static int dev_index_check(LED_TYPE type, int index) +{ +#if DEBUG + pddf_dbg(LED, "dev_index_check: type:%s[%d] index:%d num_psus:%d num_fantrays:%d\n", + LED_TYPE_STR[type], type, index, num_psus, num_fantrays); +#endif + switch(type) + { + case LED_PSU: + if(index >= MAX_PSU_NUM) return (-1); + break; + case LED_FANTRAY: + if(index >= MAX_FANTRAY_NUM) return (-1); + break; + default: + if(index >= 1) return (-1); + break; + } + return (0); +} + +static LED_OPS_DATA* find_led_ops_data(struct device_attribute *da) +{ + struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; + LED_OPS_DATA* ptr = (LED_OPS_DATA*)_ptr->addr; + LED_TYPE led_type; + if(!ptr || strlen(ptr->device_name) == 0 ) return (NULL); + + if((led_type=get_dev_type(ptr->device_name)) == LED_TYPE_MAX) { + printk(KERN_ERR "PDDF_LED ERROR *%s Unsupported Led Type\n", __func__); + return (NULL); + } + if(dev_index_check(led_type, ptr->index) == -1) { + printk(KERN_ERR "PDDF_LED ERROR %s invalid index: %d for type:%s;%d\n", __func__, ptr->index, ptr->device_name, led_type); + return (NULL); + } +#if DEBUG > 1 + pddf_dbg(LED, "find_led_ops_data: name:%s; index=%d tempAddr:%p actualAddr:%p\n", + ptr->device_name, ptr->index, ptr, dev_list[led_type]+ptr->index); +#endif + return (dev_list[led_type]+ptr->index); +} + +static void print_led_data(LED_OPS_DATA *ptr, LED_STATUS state) +{ + int i = 0; + if(!ptr) return ; + pddf_dbg(LED, KERN_INFO "Print %s index:%d num_psus:%d num_fantrays:%d ADDR=%p\n", + ptr->device_name, ptr->index, num_psus, num_fantrays, ptr); + pddf_dbg(LED, KERN_INFO "\tindex: %d\n", ptr->index); + pddf_dbg(LED, KERN_INFO "\tdevtype/devname: %s:%s\n", ptr->attr_devtype, ptr->attr_devname); + pddf_dbg(LED, KERN_INFO "\tcur_state: %d; %s \n", ptr->cur_state.state, ptr->cur_state.color); + for (i = 0; i< MAX_LED_STATUS; i++) { + if(ptr->data[i].swpld_addr && (i == state || state == -1)) { + pddf_dbg(LED, KERN_INFO "\t\t[%s]: addr/offset:0x%x;0x%x color:%s; value:[%s][0x%x][0x%x] mask_bits: 0x%x;" + "pos:%d attr_devtype:%s attr_devname:%s\n",LED_STATUS_STR[i], ptr->data[i].swpld_addr, + ptr->data[i].swpld_addr_offset, LED_STATUS_STR[i], ptr->data[i].value, + ptr->data[i].reg_values[0], ptr->data[i].reg_values[1], ptr->data[i].bits.mask_bits, + ptr->data[i].bits.pos, ptr->data[i].attr_devtype, ptr->data[i].attr_devname); + } + } +} + +ssize_t get_status_led(struct device_attribute *da) +{ + int ret=0; + struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; + LED_OPS_DATA* temp_data_ptr=(LED_OPS_DATA*)_ptr->addr; + LED_OPS_DATA* ops_ptr=find_led_ops_data(da); + uint32_t color_val=0, sys_val=0; + int state=0; + int cpld_type=0; + int j; + + if (!ops_ptr) { + pddf_dbg(LED, KERN_ERR "ERROR %s: Cannot find LED Ptr", __func__); + return (-1); + } + + if (ops_ptr->swpld_addr == 0x0) { + pddf_dbg(LED, KERN_ERR "ERROR %s: device: %s %d not configured\n", __func__, + temp_data_ptr->device_name, temp_data_ptr->index); + return (-1); + } + + if (strcmp(ops_ptr->attr_devtype, "cpld") == 0) { + cpld_type = 1; + sys_val = board_i2c_cpld_read_new(ops_ptr->swpld_addr, ops_ptr->attr_devname, ops_ptr->swpld_addr_offset); + } else if (strcmp(ops_ptr->attr_devtype, "fpgai2c") == 0) { + sys_val = board_i2c_fpga_read(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + } else { + pddf_dbg(LED, KERN_ERR "ERROR %s: %s %d devtype:%s 0x%x:0x%x not configured\n",__func__, + ops_ptr->device_name, ops_ptr->index, ops_ptr->attr_devtype, ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + return (-1); + } + + if (sys_val < 0) { + pddf_dbg(LED, KERN_ERR "ERROR %s: %s %d devtype:%s 0x%x:0x%x read failed\n",__func__, + ops_ptr->device_name, ops_ptr->index, ops_ptr->attr_devtype, ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + return sys_val; + } + + strcpy(temp_data.cur_state.color, "None"); + for (state=0; statedata[state].bits.mask_bits); + for (j = 0; j < VALUE_SIZE && ops_ptr->data[state].reg_values[j] != 0xff; j++) { + if ((color_val ^ (ops_ptr->data[state].reg_values[j] << ops_ptr->data[state].bits.pos)) == 0) { + strcpy(temp_data.cur_state.color, LED_STATUS_STR[state]); + break; + } + } + } +#if DEBUG + pddf_dbg(LED, KERN_ERR "Get : %s:%d addr/offset:0x%x; 0x%x devtype:%s;%s value=0x%x [%s]\n", + ops_ptr->device_name, ops_ptr->index, ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset, + ops_ptr->attr_devtype, cpld_type? "cpld": "fpgai2c", sys_val, temp_data.cur_state.color); +#endif + return(ret); +} + +ssize_t set_status_led(struct device_attribute *da) +{ + int ret=0; + uint32_t sys_val=0, new_val=0, read_val=0; + LED_STATUS cur_state = MAX_LED_STATUS; + struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; + LED_OPS_DATA* temp_data_ptr=(LED_OPS_DATA*)_ptr->addr; + LED_OPS_DATA* ops_ptr=find_led_ops_data(da); + char* _buf=temp_data_ptr->cur_state.color; + int cpld_type = 0; + + if (!ops_ptr) { + pddf_dbg(LED, KERN_ERR "PDDF_LED ERROR %s: Cannot find LED Ptr", __func__); + return (-1); + } + + if (ops_ptr->swpld_addr == 0x0) { + pddf_dbg(LED, KERN_ERR "PDDF_LED ERROR %s: device: %s %d not configured\n", + __func__, ops_ptr->device_name, ops_ptr->index); + return (-1); + } + + pddf_dbg(LED, KERN_ERR "%s: Set [%s;%d] color[%s]\n", __func__, + temp_data_ptr->device_name, temp_data_ptr->index, + temp_data_ptr->cur_state.color); + cur_state = find_state_index(_buf); + + if (cur_state == MAX_LED_STATUS) { + pddf_dbg(LED, KERN_ERR "ERROR %s: not supported: %s\n", _buf, __func__); + return (-1); + } + + if (ops_ptr->data[cur_state].swpld_addr != 0x0) { + if (strcmp(ops_ptr->data[cur_state].attr_devtype, "cpld") == 0) { + cpld_type = 1; + sys_val = board_i2c_cpld_read_new(ops_ptr->swpld_addr, ops_ptr->attr_devname, ops_ptr->swpld_addr_offset); + } else if (strcmp(ops_ptr->data[cur_state].attr_devtype, "fpgai2c") == 0) { + sys_val = board_i2c_fpga_read(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + } else { + pddf_dbg(LED, KERN_ERR "ERROR %s: %s %d devtype:%s not configured\n",__func__, + ops_ptr->device_name, ops_ptr->index, ops_ptr->attr_devtype); + return (-1); + } + + if (sys_val < 0){ + return sys_val; + } + + + new_val = (sys_val & ops_ptr->data[cur_state].bits.mask_bits) | + (ops_ptr->data[cur_state].reg_values[0] << ops_ptr->data[cur_state].bits.pos); + } else { + pddf_dbg(LED, KERN_ERR "ERROR %s: %s %d devtype:%s state %d; %s not configured\n",__func__, + ops_ptr->device_name, ops_ptr->index, ops_ptr->attr_devtype, cur_state, _buf); + return (-1); + } + + if (strcmp(ops_ptr->data[cur_state].attr_devtype, "cpld") == 0) { + ret = board_i2c_cpld_write_new(ops_ptr->swpld_addr, ops_ptr->attr_devname, ops_ptr->swpld_addr_offset, new_val); + read_val = board_i2c_cpld_read_new(ops_ptr->swpld_addr, ops_ptr->attr_devname, ops_ptr->swpld_addr_offset); + } else if (strcmp(ops_ptr->data[cur_state].attr_devtype, "fpgai2c") == 0) { + ret = board_i2c_fpga_write(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset, (uint8_t)new_val); + read_val = board_i2c_fpga_read(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + } else { + pddf_dbg(LED, KERN_ERR "ERROR %s: %s %d devtype:%s not configured\n",__func__, + ops_ptr->device_name, ops_ptr->index, ops_ptr->attr_devtype); + return (-1); + } + +#if DEBUG + pddf_dbg(LED, KERN_ERR "Set color:%s; 0x%x:0x%x sys_val:0x%x new_val:0x%x devtype:%s w_ret:0x%x read:0x%x devtype:%s\n", + LED_STATUS_STR[cur_state], ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset, sys_val, new_val, + cpld_type? "cpld":"fpgai2c", ret, read_val, ops_ptr->data[cur_state].attr_devtype); +#endif + + return(ret); +} + + +ssize_t show_pddf_data(struct device *dev, struct device_attribute *da, + char *buf) +{ + int ret = 0; + struct pddf_data_attribute *ptr = (struct pddf_data_attribute *)da; + switch (ptr->type) + { + case PDDF_CHAR: + ret = sprintf(buf, "%s\n", ptr->addr); + break; + case PDDF_INT_DEC: + ret = sprintf(buf, "%d\n", *(int*)(ptr->addr)); + break; + case PDDF_INT_HEX: + ret = sprintf(buf, "0x%x\n", *(int*)(ptr->addr)); + break; + case PDDF_USHORT: + ret = sprintf(buf, "0x%x\n", *(unsigned short *)(ptr->addr)); + break; + case PDDF_UINT32: + ret = sprintf(buf, "0x%x\n", *(uint32_t *)(ptr->addr)); + break; + default: + break; + } +#if DEBUG > 1 + pddf_dbg(LED, "[ READ ] DATA ATTR PTR [%s] TYPE:%d, Value:[%s]\n", + ptr->dev_attr.attr.name, ptr->type, buf); +#endif + return ret; +} + +ssize_t store_pddf_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count) +{ + int ret = 0, num = 0; + struct pddf_data_attribute *ptr = (struct pddf_data_attribute *)da; + switch (ptr->type) + { + case PDDF_CHAR: + strncpy(ptr->addr, buf, strlen(buf)-1); // to discard newline char form buf + ptr->addr[strlen(buf)-1] = '\0'; +#if DEBUG + pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR [%s] PDDF_CHAR VALUE:%s\n", + ptr->dev_attr.attr.name, ptr->addr); +#endif + break; + case PDDF_INT_DEC: + ret = kstrtoint(buf,10,&num); + if (ret==0) + *(int *)(ptr->addr) = num; +#if DEBUG + pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR [%s] PDDF_DEC VALUE:%d\n", + ptr->dev_attr.attr.name, *(int *)(ptr->addr)); +#endif + break; + case PDDF_INT_HEX: + ret = kstrtoint(buf,16,&num); + if (ret==0) + *(int *)(ptr->addr) = num; +#if DEBUG + pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR [%s] PDDF_HEX VALUE:0x%x\n", + ptr->dev_attr.attr.name, *(int *)(ptr->addr)); +#endif + break; + case PDDF_USHORT: + ret = kstrtoint(buf,16,&num); + if (ret==0) + *(unsigned short *)(ptr->addr) = (unsigned short)num; +#if DEBUG + pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR [%s] PDDF_USHORT VALUE:%x\n", + ptr->dev_attr.attr.name, *(unsigned short *)(ptr->addr)); +#endif + break; + case PDDF_UINT32: + ret = kstrtoint(buf,16,&num); + if (ret==0) + *(uint32_t *)(ptr->addr) = (uint32_t)num; +#if DEBUG + pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR [%s] PDDF_UINT32 VALUE:%d\n", + ptr->dev_attr.attr.name, *(uint32_t *)(ptr->addr)); +#endif + break; + default: + break; + } + return count; +} + +ssize_t show_pddf_s3ip_data(struct device *dev, struct device_attribute *da, + char *buf) +{ + int ret = 0; + pddf_dbg(LED, KERN_ERR " %s", __FUNCTION__); + struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; + if (_ptr == NULL) { + pddf_dbg(LED, KERN_ERR "%s return", __FUNCTION__); + return -1; + } + LED_OPS_DATA* ops_ptr=(LED_OPS_DATA*)_ptr->addr; + uint32_t color_val=0, sys_val=0; + int state=0, j; + int cpld_type=0; + if (!ops_ptr) { + pddf_dbg(LED, KERN_ERR "ERROR %s: Cannot find LED Ptr", __func__); + return (-1); + } + if (ops_ptr->swpld_addr == 0x0) { + pddf_dbg(LED, KERN_ERR "ERROR %s: device: %s %d not configured\n", __func__, + ops_ptr->device_name, ops_ptr->index); + return (-1); + } + if ( strcmp(ops_ptr->attr_devtype, "cpld") == 0) { + cpld_type=1; + sys_val = board_i2c_cpld_read(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + } else if ( strcmp(ops_ptr->attr_devtype, "fpgai2c") == 0) { + sys_val = board_i2c_fpga_read(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + } else { + pddf_dbg(LED, KERN_ERR "ERROR %s: %s %d devtype:%s 0x%x:0x%x not configured\n",__func__, + ops_ptr->device_name, ops_ptr->index, ops_ptr->attr_devtype, ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + return (-1); + } + + if (sys_val < 0) { + pddf_dbg(LED, KERN_ERR "ERROR %s: %s %d devtype:%s 0x%x:0x%x read failed\n",__func__, + ops_ptr->device_name, ops_ptr->index, ops_ptr->attr_devtype, ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + return sys_val; + } + for (state=0; statedata[state].bits.mask_bits); + for (j = 0; j < VALUE_SIZE && ops_ptr->data[state].reg_values[j] != 0xff; j++) { + if ((color_val ^ (ops_ptr->data[state].reg_values[j] << ops_ptr->data[state].bits.pos))==0) { + ret = sprintf(buf, "%d\n", state); + break; + } + } + } +#if DEBUG + pddf_dbg(LED, KERN_ERR "Get : %s:%d addr/offset:0x%x; 0x%x devtype:%s;%s value=0x%x [%d]\n", + ops_ptr->device_name, ops_ptr->index, ops_ptr->swpld_addr, + ops_ptr->swpld_addr_offset, ops_ptr->attr_devtype, cpld_type? "cpld": "fpgai2c", sys_val, state); +#endif + return ret; +} + +ssize_t store_pddf_s3ip_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count) +{ + int ret = 0; + int cur_state = 0; + uint32_t sys_val=0, new_val=0, read_val=0; + int cpld_type=0; + + pddf_dbg(LED, KERN_ERR "%s: %s;%d", __FUNCTION__, buf, cur_state); + struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; + ret = kstrtoint(buf,10,&cur_state); + if (_ptr == NULL || cur_state >= MAX_LED_STATUS || ret !=0) { + pddf_dbg(LED, KERN_ERR "%s return", __FUNCTION__); + return -1; + } + LED_OPS_DATA* ops_ptr=(LED_OPS_DATA*)_ptr->addr; + + if ( strcmp(ops_ptr->attr_devtype, "cpld") == 0) { + cpld_type=1; + sys_val = board_i2c_cpld_read(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + } else if ( strcmp(ops_ptr->attr_devtype, "fpgai2c") == 0) { + sys_val = board_i2c_fpga_read(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + } else { + pddf_dbg(LED, KERN_ERR "ERROR %s: %s %d devtype:%s 0x%x:0x%x not configured\n",__func__, + ops_ptr->device_name, ops_ptr->index, ops_ptr->attr_devtype, ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + return (-1); + } + + new_val = (sys_val & ops_ptr->data[cur_state].bits.mask_bits) | + (ops_ptr->data[cur_state].reg_values[0] << ops_ptr->data[cur_state].bits.pos); + + if ( strcmp(ops_ptr->data[cur_state].attr_devtype, "cpld") == 0) { + ret = board_i2c_cpld_write(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset, new_val); + read_val = board_i2c_cpld_read(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + } else if ( strcmp(ops_ptr->data[cur_state].attr_devtype, "fpgai2c") == 0) { + ret = board_i2c_fpga_write(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset, (uint8_t)new_val); + read_val = board_i2c_fpga_read(ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset); + } else { + pddf_dbg(LED, KERN_ERR "ERROR %s: %s %d devtype:%s not configured\n",__func__, + ops_ptr->device_name, ops_ptr->index, ops_ptr->attr_devtype); + return (-1); + } + +#if DEBUG + pddf_dbg(LED, KERN_INFO "Set color:%s; 0x%x:0x%x sys_val:0x%x new_val:0x%x devtype:%s w_ret:0x%x read:0x%x devtype:%s\n", + LED_STATUS_STR[cur_state], ops_ptr->swpld_addr, ops_ptr->swpld_addr_offset, + sys_val, new_val, cpld_type? "cpld":"fpgai2c", ret, read_val, ops_ptr->data[cur_state].attr_devtype); +#endif + return count; +} + + +static int load_led_ops_data(struct device_attribute *da, LED_STATUS state) +{ + struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; + LED_OPS_DATA* ptr = (LED_OPS_DATA*)_ptr->addr; + LED_TYPE led_type; + LED_OPS_DATA* ops_ptr = NULL; + int i = 0; + char *token = NULL, *value_ptr = NULL; + + if(!ptr || strlen(ptr->device_name)==0 ) { + pddf_dbg(LED, KERN_INFO "SYSTEM_LED: load_led_ops_data return -1 device_name:%s\n", ptr? ptr->device_name:"NULL"); + return(-1); + } + + if(ptr->device_name) + { + pddf_dbg(LED, KERN_INFO "[%s]: load_led_ops_data: index=%d addr=0x%x;0x%x devtype:%s devname=%s valu=%s\n", + ptr->device_name, ptr->index, ptr->swpld_addr, ptr->swpld_addr_offset, ptr->attr_devtype, ptr->attr_devname, ptr->data[0].value); + } + if((led_type=get_dev_type(ptr->device_name))==LED_TYPE_MAX) { + pddf_dbg(LED, KERN_ERR "PDDF_LED ERROR *%s Unsupported Led Type\n", __func__); + return(-1); + } + if(dev_index_check(led_type, ptr->index)==-1) { + pddf_dbg(LED, KERN_ERR "PDDF_LED ERROR %s invalid index: %d for type:%d\n", __func__, ptr->index, led_type); + return(-1); + } + ops_ptr = dev_list[led_type]+ptr->index; + + memcpy(ops_ptr->device_name, ptr->device_name, sizeof(ops_ptr->device_name)); + ops_ptr->index = ptr->index; + memcpy(&ops_ptr->data[state], &ptr->data[0], sizeof(LED_DATA)); + ops_ptr->data[state].swpld_addr = ptr->swpld_addr; + ops_ptr->data[state].swpld_addr_offset = ptr->swpld_addr_offset; + ops_ptr->swpld_addr = ptr->swpld_addr; + ops_ptr->swpld_addr_offset = ptr->swpld_addr_offset; + memcpy(ops_ptr->data[state].attr_devtype, ptr->attr_devtype, sizeof(ops_ptr->data[state].attr_devtype)); + memcpy(ops_ptr->data[state].attr_devname, ptr->attr_devname, sizeof(ops_ptr->data[state].attr_devname)); + memcpy(ops_ptr->attr_devtype, ptr->attr_devtype, sizeof(ops_ptr->attr_devtype)); + memcpy(ops_ptr->attr_devname, ptr->attr_devname, sizeof(ops_ptr->attr_devname)); +#ifdef __STDC_LIB_EXT1__ + memset_s(ops_ptr->data[state].reg_values, sizeof(ops_ptr->data[state].reg_values), 0xff, sizeof(ops_ptr->data[state].reg_values)); +#else + memset(ops_ptr->data[state].reg_values, 0xff, sizeof(ops_ptr->data[state].reg_values)); +#endif + value_ptr = kzalloc(sizeof(ops_ptr->data[state].value), GFP_KERNEL); + if (value_ptr) { + memcpy(value_ptr, ops_ptr->data[state].value, sizeof(ops_ptr->data[state].value)); + while((token = strsep((char**)&value_ptr,";")) != NULL && i < VALUE_SIZE) { + if (kstrtou8(token, 16, &ops_ptr->data[state].reg_values[i])) { + pddf_dbg(LED, KERN_ERR "load_led_ops_data: [%s] conversion error\n", token); + } + i++; + } + kfree(value_ptr); + } + + print_led_data(dev_list[led_type]+ptr->index, state); + + memset(ptr, 0, sizeof(LED_OPS_DATA)); + return (0); +} + +static int show_led_ops_data(struct device_attribute *da) +{ + LED_OPS_DATA* ops_ptr = find_led_ops_data(da); + print_led_data(ops_ptr, -1); + return(0); +} + +static int verify_led_ops_data(struct device_attribute *da) +{ + struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; + LED_OPS_DATA* ptr=(LED_OPS_DATA*)_ptr->addr; + LED_OPS_DATA* ops_ptr=find_led_ops_data(da); + + if(ops_ptr) + memcpy(ptr, ops_ptr, sizeof(LED_OPS_DATA)); + else + { + pddf_dbg(LED, "SYSTEM_LED: verify_led_ops_data: Failed to find ops_ptr name:%s; index=%d\n", ptr->device_name, ptr->index); + } + return (0); +} + + +ssize_t dev_operation(struct device *dev, struct device_attribute *da, const char *buf, size_t count) +{ +#if DEBUG + pddf_dbg(LED, KERN_INFO "dev_operation [%s]\n", buf); +#endif + if(strncmp(buf, "show", strlen("show")) == 0) { + show_led_ops_data(da); + } + else if(strncmp(buf, "verify", strlen("verify")) == 0) { + verify_led_ops_data(da); + } + else if(strncmp(buf, "get_status", strlen("get_status")) == 0) { + get_status_led(da); + } + else if(strncmp(buf, "set_status", strlen("set_status")) == 0) { + set_status_led(da); + } + else { + LED_STATUS index = find_state_index(buf); + if (index < MAX_LED_STATUS) { + load_led_ops_data(da, index); + } else { + printk(KERN_ERR "PDDF_ERROR: %s: Invalid value for dev_ops %s\n", __FUNCTION__, buf); + } + } + return (count); +} + +ssize_t store_config_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count) +{ + int ret, num; + struct pddf_data_attribute *ptr = (struct pddf_data_attribute *)da; + if(strncmp(ptr->dev_attr.attr.name, "num_psus", strlen("num_psus")) == 0) { + ret = kstrtoint(buf,10,&num); + if (ret==0) + *(int *)(ptr->addr) = num; +#if DEBUG + pddf_dbg(LED, "[ WRITE ] ATTR CONFIG [%s] VALUE:%d; %d\n", + ptr->dev_attr.attr.name, num, num_psus); +#endif + return(count); + } + if (strncmp(ptr->dev_attr.attr.name, "num_fantrays", strlen("num_fantrays")) ==0) { + ret = kstrtoint(buf, 10, &num); + if (ret == 0) + *(int *)(ptr->addr) = num; +#if DEBUG + pddf_dbg(LED, "[ WRITE ] ATTR CONFIG [%s] VALUE:%d; %d\n", + ptr->dev_attr.attr.name, num, num_fantrays); +#endif + return (count); + } + return (count); +} + +ssize_t store_bits_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count) +{ + int len = 0, num1 = 0, num2 = 0, i=0, rc1=0, rc2=0; + char mask=0xFF; + char *pptr=NULL; + char bits[NAME_SIZE]; + struct pddf_data_attribute *ptr = (struct pddf_data_attribute *)da; + MASK_BITS* bits_ptr=(MASK_BITS*)(ptr->addr); + strncpy(bits_ptr->bits, buf, strlen(buf)-1); // to discard newline char form buf + bits_ptr->bits[strlen(buf)-1] = '\0'; + if((pptr=strstr(buf,":")) != NULL) { + len = pptr-buf; + sprintf(bits, buf); + bits[len] = '\0'; + rc1 = kstrtoint(bits, 16, &num1); + if (rc1 == 0) + { + sprintf(bits, ++pptr); + rc2 = kstrtoint(bits, 16, &num2); + if (rc2 == 0) + { + for (i=num2; i<=num1; i++) { + mask &= ~(1 << i); + } + bits_ptr->mask_bits = mask; + bits_ptr->pos = num2; + } + } + } else { + rc1 = kstrtoint(buf, 16, &num1); + if (rc1 == 0) + { + bits_ptr->mask_bits = mask & ~(1 << num1); + bits_ptr->pos = num1; + } + } +#if DEBUG + pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR Bits [%s] VALUE:%s mask:0x%x; pos:0x%x\n", + ptr->dev_attr.attr.name, bits_ptr->bits, bits_ptr->mask_bits, bits_ptr->pos); +#endif + return (count); +} + +/************************************************************************** + * platform/ attributes + **************************************************************************/ +PDDF_LED_DATA_ATTR(platform, num_psus, S_IWUSR|S_IRUGO, show_pddf_data, + store_config_data, PDDF_INT_DEC, sizeof(int), (void*)&num_psus); +PDDF_LED_DATA_ATTR(platform, num_fantrays, S_IWUSR|S_IRUGO, show_pddf_data, + store_config_data, PDDF_INT_DEC, sizeof(int), (void*)&num_fantrays); + +struct attribute* attrs_platform[]={ + &pddf_dev_platform_attr_num_psus.dev_attr.attr, + &pddf_dev_platform_attr_num_fantrays.dev_attr.attr, + NULL, +}; +struct attribute_group attr_group_platform={ + .attrs = attrs_platform, +}; + +/************************************************************************** + * led/ attributes + **************************************************************************/ +PDDF_LED_DATA_ATTR(dev, device_name, S_IWUSR|S_IRUGO, show_pddf_data, + store_pddf_data, PDDF_CHAR, NAME_SIZE, (void*)&temp_data.device_name); +PDDF_LED_DATA_ATTR(dev, attr_devtype, S_IWUSR|S_IRUGO, show_pddf_data, + store_pddf_data, PDDF_CHAR, NAME_SIZE, (void*)&temp_data.attr_devtype); +PDDF_LED_DATA_ATTR(dev, attr_devname, S_IWUSR|S_IRUGO, show_pddf_data, + store_pddf_data, PDDF_CHAR, NAME_SIZE, (void*)&temp_data.attr_devname); +PDDF_LED_DATA_ATTR(dev, index, S_IWUSR|S_IRUGO, show_pddf_data, + store_pddf_data, PDDF_INT_DEC, sizeof(int), (void*)&temp_data.index); +PDDF_LED_DATA_ATTR(dev, swpld_addr, S_IWUSR|S_IRUGO, show_pddf_data, + store_pddf_data, PDDF_INT_HEX, sizeof(int), (void*)&temp_data.swpld_addr); +PDDF_LED_DATA_ATTR(dev, swpld_addr_offset, S_IWUSR|S_IRUGO, show_pddf_data, + store_pddf_data, PDDF_INT_HEX, sizeof(int), (void*)&temp_data.swpld_addr_offset); +PDDF_LED_DATA_ATTR(dev, dev_ops , S_IWUSR, NULL, + dev_operation, PDDF_CHAR, NAME_SIZE, (void*)&temp_data); + +struct attribute* attrs_dev[] = { + &pddf_dev_dev_attr_device_name.dev_attr.attr, + &pddf_dev_dev_attr_attr_devtype.dev_attr.attr, + &pddf_dev_dev_attr_attr_devname.dev_attr.attr, + &pddf_dev_dev_attr_index.dev_attr.attr, + &pddf_dev_dev_attr_swpld_addr.dev_attr.attr, + &pddf_dev_dev_attr_swpld_addr_offset.dev_attr.attr, + &pddf_dev_dev_attr_dev_ops.dev_attr.attr, + NULL, +}; + +struct attribute_group attr_group_dev = { + .attrs = attrs_dev, +}; + +/************************************************************************** + * state_attr/ attributes + **************************************************************************/ +#define LED_DEV_STATE_ATTR_GROUP(name, func) \ + PDDF_LED_DATA_ATTR(name, bits, S_IWUSR|S_IRUGO, show_pddf_data, \ + store_bits_data, PDDF_CHAR, NAME_SIZE, func.bits.bits); \ + PDDF_LED_DATA_ATTR(name, value, S_IWUSR|S_IRUGO, show_pddf_data, \ + store_pddf_data, PDDF_CHAR, NAME_SIZE, func.value); \ + struct attribute* attrs_##name[]={ \ + &pddf_dev_##name##_attr_bits.dev_attr.attr, \ + &pddf_dev_##name##_attr_value.dev_attr.attr, \ + NULL, \ + }; \ + struct attribute_group attr_group_##name={ \ + .attrs = attrs_##name, \ + }; \ + + +LED_DEV_STATE_ATTR_GROUP(state_attr, (void*)&temp_data.data[0]) + +/************************************************************************** + * cur_state/ attributes + **************************************************************************/ +PDDF_LED_DATA_ATTR(cur_state, color, S_IWUSR|S_IRUGO, show_pddf_data, + store_pddf_data, PDDF_CHAR, NAME_SIZE, (void*)&temp_data.cur_state.color); +PDDF_LED_DATA_ATTR(cur_state, sys_led, S_IWUSR|S_IRUGO, show_pddf_s3ip_data, + store_pddf_s3ip_data, PDDF_INT_DEC, sizeof(int), (void*)&sys_led_ops_data); +PDDF_LED_DATA_ATTR(cur_state, loc_led, S_IWUSR|S_IRUGO, show_pddf_s3ip_data, + store_pddf_s3ip_data, PDDF_INT_DEC, NAME_SIZE, (void*)&loc_led_ops_data); +PDDF_LED_DATA_ATTR(cur_state, bmc_led, S_IWUSR|S_IRUGO, show_pddf_s3ip_data, + store_pddf_s3ip_data, PDDF_INT_DEC, sizeof(int), (void*)&bmc_led_ops_data); +PDDF_LED_DATA_ATTR(cur_state, fan_led, S_IWUSR|S_IRUGO, show_pddf_s3ip_data, + store_pddf_s3ip_data, PDDF_INT_DEC, sizeof(int), (void*)&fan_led_ops_data); +PDDF_LED_DATA_ATTR(cur_state, psu_led, S_IWUSR|S_IRUGO, show_pddf_s3ip_data, + store_pddf_s3ip_data, PDDF_INT_DEC, sizeof(int), NULL); +PDDF_LED_DATA_ATTR(cur_state, psu1_led, S_IRUGO, show_pddf_s3ip_data, + NULL, PDDF_INT_DEC, sizeof(int), (void *)&psu_led_ops_data[0]); +PDDF_LED_DATA_ATTR(cur_state, psu2_led, S_IRUGO, show_pddf_s3ip_data, + NULL, PDDF_INT_DEC, sizeof(int), (void *)&psu_led_ops_data[1]); +PDDF_LED_DATA_ATTR(cur_state, fantray1_led, S_IWUSR|S_IRUGO, show_pddf_s3ip_data, + store_pddf_s3ip_data, PDDF_INT_DEC, sizeof(int), (void *)&fantray_led_ops_data[0]); +PDDF_LED_DATA_ATTR(cur_state, fantray2_led, S_IWUSR|S_IRUGO, show_pddf_s3ip_data, + store_pddf_s3ip_data, PDDF_INT_DEC, sizeof(int), (void *)&fantray_led_ops_data[1]); +PDDF_LED_DATA_ATTR(cur_state, fantray3_led, S_IWUSR|S_IRUGO, show_pddf_s3ip_data, + store_pddf_s3ip_data, PDDF_INT_DEC, sizeof(int), (void *)&fantray_led_ops_data[2]); +PDDF_LED_DATA_ATTR(cur_state, fantray4_led, S_IWUSR|S_IRUGO, show_pddf_s3ip_data, + store_pddf_s3ip_data, PDDF_INT_DEC, sizeof(int), (void *)&fantray_led_ops_data[3]); +PDDF_LED_DATA_ATTR(cur_state, fantray5_led, S_IWUSR|S_IRUGO, show_pddf_s3ip_data, + store_pddf_s3ip_data, PDDF_INT_DEC, sizeof(int), (void *)&fantray_led_ops_data[4]); +PDDF_LED_DATA_ATTR(cur_state, fantray6_led, S_IWUSR|S_IRUGO, show_pddf_s3ip_data, + store_pddf_s3ip_data, PDDF_INT_DEC, sizeof(int), (void *)&fantray_led_ops_data[5]); +PDDF_LED_DATA_ATTR(cur_state, fantray7_led, S_IWUSR|S_IRUGO, show_pddf_s3ip_data, + store_pddf_s3ip_data, PDDF_INT_DEC, sizeof(int), (void *)&fantray_led_ops_data[6]); + + +struct attribute* attrs_cur_state[] = { + &pddf_dev_cur_state_attr_color.dev_attr.attr, + &pddf_dev_cur_state_attr_sys_led.dev_attr.attr, + &pddf_dev_cur_state_attr_loc_led.dev_attr.attr, + &pddf_dev_cur_state_attr_bmc_led.dev_attr.attr, + &pddf_dev_cur_state_attr_fan_led.dev_attr.attr, + &pddf_dev_cur_state_attr_psu_led.dev_attr.attr, + &pddf_dev_cur_state_attr_psu1_led.dev_attr.attr, + &pddf_dev_cur_state_attr_psu2_led.dev_attr.attr, + &pddf_dev_cur_state_attr_fantray1_led.dev_attr.attr, + &pddf_dev_cur_state_attr_fantray2_led.dev_attr.attr, + &pddf_dev_cur_state_attr_fantray3_led.dev_attr.attr, + &pddf_dev_cur_state_attr_fantray4_led.dev_attr.attr, + &pddf_dev_cur_state_attr_fantray5_led.dev_attr.attr, + &pddf_dev_cur_state_attr_fantray6_led.dev_attr.attr, + &pddf_dev_cur_state_attr_fantray7_led.dev_attr.attr, + NULL, +}; + +struct attribute_group attr_group_cur_state={ + .attrs = attrs_cur_state, +}; + +/*************************************************************************/ +#define KOBJ_FREE(obj) \ + if(obj) kobject_put(obj); \ + +void free_kobjs(void) +{ + KOBJ_FREE(cur_state_kobj) + KOBJ_FREE(state_attr_kobj) + KOBJ_FREE(led_kobj) + KOBJ_FREE(platform_kobj) +} + +int KBOJ_CREATE(char* name, struct kobject* parent, struct kobject** child) +{ + if (parent) { + *child = kobject_create_and_add(name, parent); + } else { + printk(KERN_ERR "PDDF_LED ERROR to create %s kobj; null parent\n", name); + free_kobjs(); + return (-ENOMEM); + } + return (0); +} + +int LED_DEV_ATTR_CREATE(struct kobject *kobj, const struct attribute_group *attr, const char* name) +{ + int status = sysfs_create_group(kobj, attr); + if(status) { + pddf_dbg(LED, KERN_ERR "Driver ERROR: sysfs_create %s failed rc=%d\n", name, status); + } + return (status); +} + + +static int __init led_init(void) { + struct kobject *device_kobj; + pddf_dbg(LED, KERN_INFO "PDDF GENERIC LED MODULE init..\n"); + + device_kobj = get_device_i2c_kobj(); + if(!device_kobj) + return -ENOMEM; + + KBOJ_CREATE("platform", device_kobj, &platform_kobj); + KBOJ_CREATE("led", device_kobj, &led_kobj); + KBOJ_CREATE("state_attr", led_kobj, &state_attr_kobj); + KBOJ_CREATE("cur_state", led_kobj, &cur_state_kobj); + + LED_DEV_ATTR_CREATE(platform_kobj, &attr_group_platform, "attr_group_platform"); + LED_DEV_ATTR_CREATE(led_kobj, &attr_group_dev, "attr_group_dev"); + LED_DEV_ATTR_CREATE(state_attr_kobj, &attr_group_state_attr, "attr_group_state_attr"); + LED_DEV_ATTR_CREATE(cur_state_kobj, &attr_group_cur_state, "attr_group_cur_state"); + return (0); +} + + +static void __exit led_exit(void) { + pddf_dbg(LED, "PDDF GENERIC LED MODULE exit..\n"); + free_kobjs(); +} + +module_init(led_init); +module_exit(led_exit); + +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("led driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/lm75.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/lm75.c new file mode 100644 index 000000000000..42e4126b754a --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/lm75.c @@ -0,0 +1,958 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * lm75.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998, 1999 Frodo Looijaard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lm75.h" + +/* + * This driver handles the LM75 and compatible digital temperature sensors. + */ + +enum lm75_type { /* keep sorted in alphabetical order */ + adt75, + at30ts74, + ds1775, + ds75, + ds7505, + g751, + lm75, + lm75a, + lm75b, + max6625, + max6626, + max31725, + mcp980x, + pct2075, + stds75, + stlm75, + tcn75, + tmp100, + tmp101, + tmp105, + tmp112, + tmp175, + tmp275, + tmp75, + tmp75b, + tmp75c, + tmp1075, +}; + +/** + * struct lm75_params - lm75 configuration parameters. + * @set_mask: Bits to set in configuration register when configuring + * the chip. + * @clr_mask: Bits to clear in configuration register when configuring + * the chip. + * @default_resolution: Default number of bits to represent the temperature + * value. + * @resolution_limits: Limit register resolution. Optional. Should be set if + * the resolution of limit registers does not match the + * resolution of the temperature register. + * @resolutions: List of resolutions associated with sample times. + * Optional. Should be set if num_sample_times is larger + * than 1, and if the resolution changes with sample times. + * If set, number of entries must match num_sample_times. + * @default_sample_time:Sample time to be set by default. + * @num_sample_times: Number of possible sample times to be set. Optional. + * Should be set if the number of sample times is larger + * than one. + * @sample_times: All the possible sample times to be set. Mandatory if + * num_sample_times is larger than 1. If set, number of + * entries must match num_sample_times. + */ + +struct lm75_params { + u8 set_mask; + u8 clr_mask; + u8 default_resolution; + u8 resolution_limits; + const u8 *resolutions; + unsigned int default_sample_time; + u8 num_sample_times; + const unsigned int *sample_times; +}; + +/* Addresses scanned */ +static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; + +/* The LM75 registers */ +#define LM75_REG_TEMP 0x00 +#define LM75_REG_CONF 0x01 +#define LM75_REG_HYST 0x02 +#define LM75_REG_MAX 0x03 +#define PCT2075_REG_IDLE 0x04 + +/* Each client has this additional data */ +struct lm75_data { + struct i2c_client *client; + struct regmap *regmap; + struct regulator *vs; + u8 orig_conf; + u8 current_conf; + u8 resolution; /* In bits, 9 to 16 */ + unsigned int sample_time; /* In ms */ + enum lm75_type kind; + const struct lm75_params *params; +}; + +/*-----------------------------------------------------------------------*/ + +static const u8 lm75_sample_set_masks[] = { 0 << 5, 1 << 5, 2 << 5, 3 << 5 }; + +#define LM75_SAMPLE_CLEAR_MASK (3 << 5) + +/* The structure below stores the configuration values of the supported devices. + * In case of being supported multiple configurations, the default one must + * always be the first element of the array + */ +static const struct lm75_params device_params[] = { + [adt75] = { + .clr_mask = 1 << 5, /* not one-shot mode */ + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [at30ts74] = { + .set_mask = 3 << 5, /* 12-bit mode*/ + .default_resolution = 12, + .default_sample_time = 200, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 25, 50, 100, 200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [ds1775] = { + .clr_mask = 3 << 5, + .set_mask = 2 << 5, /* 11-bit mode */ + .default_resolution = 11, + .default_sample_time = 500, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 125, 250, 500, 1000 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [ds75] = { + .clr_mask = 3 << 5, + .set_mask = 2 << 5, /* 11-bit mode */ + .default_resolution = 11, + .default_sample_time = 600, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 150, 300, 600, 1200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [stds75] = { + .clr_mask = 3 << 5, + .set_mask = 2 << 5, /* 11-bit mode */ + .default_resolution = 11, + .default_sample_time = 600, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 150, 300, 600, 1200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [stlm75] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 6, + }, + [ds7505] = { + .set_mask = 3 << 5, /* 12-bit mode*/ + .default_resolution = 12, + .default_sample_time = 200, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 25, 50, 100, 200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [g751] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [lm75] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [lm75a] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [lm75b] = { + .default_resolution = 11, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [max6625] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 7, + }, + [max6626] = { + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 7, + .resolution_limits = 9, + }, + [max31725] = { + .default_resolution = 16, + .default_sample_time = MSEC_PER_SEC / 20, + }, + [tcn75] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 18, + }, + [pct2075] = { + .default_resolution = 11, + .default_sample_time = MSEC_PER_SEC / 10, + .num_sample_times = 31, + .sample_times = (unsigned int []){ 100, 200, 300, 400, 500, 600, + 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, + 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, + 2800, 2900, 3000, 3100 }, + }, + [mcp980x] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode */ + .default_resolution = 12, + .resolution_limits = 9, + .default_sample_time = 240, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 30, 60, 120, 240 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp100] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode */ + .default_resolution = 12, + .default_sample_time = 320, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 40, 80, 160, 320 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp101] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode */ + .default_resolution = 12, + .default_sample_time = 320, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 40, 80, 160, 320 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp105] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp112] = { + .set_mask = 3 << 5, /* 8 samples / second */ + .clr_mask = 1 << 7, /* no one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 125, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 125, 250, 1000, 4000 }, + }, + [tmp175] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp275] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp75] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp75b] = { /* not one-shot mode, Conversion rate 37Hz */ + .clr_mask = 1 << 7 | 3 << 5, + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 37, + .sample_times = (unsigned int []){ MSEC_PER_SEC / 37, + MSEC_PER_SEC / 18, + MSEC_PER_SEC / 9, MSEC_PER_SEC / 4 }, + .num_sample_times = 4, + }, + [tmp75c] = { + .clr_mask = 1 << 5, /*not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 12, + }, + [tmp1075] = { /* not one-shot mode, 27.5 ms sample rate */ + .clr_mask = 1 << 5 | 1 << 6 | 1 << 7, + .default_resolution = 12, + .default_sample_time = 28, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + } +}; + +static inline long lm75_reg_to_mc(s16 temp, u8 resolution) +{ + return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); +} + +static int lm75_write_config(struct lm75_data *data, u8 set_mask, + u8 clr_mask) +{ + u8 value; + + clr_mask |= LM75_SHUTDOWN; + value = data->current_conf & ~clr_mask; + value |= set_mask; + + if (data->current_conf != value) { + s32 err; + + err = i2c_smbus_write_byte_data(data->client, LM75_REG_CONF, + value); + if (err) + return err; + data->current_conf = value; + } + return 0; +} + +static int lm75_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct lm75_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err, reg; + + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + *val = data->sample_time; + break; + default: + return -EINVAL; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + reg = LM75_REG_TEMP; + break; + case hwmon_temp_max: + reg = LM75_REG_MAX; + break; + case hwmon_temp_max_hyst: + reg = LM75_REG_HYST; + break; + default: + return -EINVAL; + } + err = regmap_read(data->regmap, reg, ®val); + if (err < 0) + return err; + + *val = lm75_reg_to_mc(regval, data->resolution); + break; + default: + return -EINVAL; + } + return 0; +} + +static int lm75_write_temp(struct device *dev, u32 attr, long temp) +{ + struct lm75_data *data = dev_get_drvdata(dev); + u8 resolution; + int reg; + + switch (attr) { + case hwmon_temp_max: + reg = LM75_REG_MAX; + break; + case hwmon_temp_max_hyst: + reg = LM75_REG_HYST; + break; + default: + return -EINVAL; + } + + /* + * Resolution of limit registers is assumed to be the same as the + * temperature input register resolution unless given explicitly. + */ + if (data->params->resolution_limits) + resolution = data->params->resolution_limits; + else + resolution = data->resolution; + + temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); + temp = DIV_ROUND_CLOSEST(temp << (resolution - 8), + 1000) << (16 - resolution); + + return regmap_write(data->regmap, reg, (u16)temp); +} + +static int lm75_update_interval(struct device *dev, long val) +{ + struct lm75_data *data = dev_get_drvdata(dev); + unsigned int reg; + u8 index; + s32 err; + + index = find_closest(val, data->params->sample_times, + (int)data->params->num_sample_times); + + switch (data->kind) { + default: + err = lm75_write_config(data, lm75_sample_set_masks[index], + LM75_SAMPLE_CLEAR_MASK); + if (err) + return err; + + data->sample_time = data->params->sample_times[index]; + if (data->params->resolutions) + data->resolution = data->params->resolutions[index]; + break; + case tmp112: + err = regmap_read(data->regmap, LM75_REG_CONF, ®); + if (err < 0) + return err; + reg &= ~0x00c0; + reg |= (3 - index) << 6; + err = regmap_write(data->regmap, LM75_REG_CONF, reg); + if (err < 0) + return err; + data->sample_time = data->params->sample_times[index]; + break; + case pct2075: + err = i2c_smbus_write_byte_data(data->client, PCT2075_REG_IDLE, + index + 1); + if (err) + return err; + data->sample_time = data->params->sample_times[index]; + break; + } + return 0; +} + +static int lm75_write_chip(struct device *dev, u32 attr, long val) +{ + switch (attr) { + case hwmon_chip_update_interval: + return lm75_update_interval(dev, val); + default: + return -EINVAL; + } + return 0; +} + +static int lm75_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_chip: + return lm75_write_chip(dev, attr, val); + case hwmon_temp: + return lm75_write_temp(dev, attr, val); + default: + return -EINVAL; + } + return 0; +} + +static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct lm75_data *config_data = data; + + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + if (config_data->params->num_sample_times > 1) + return 0644; + return 0444; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_max: + case hwmon_temp_max_hyst: + return 0644; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info *lm75_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST), + NULL +}; + +static const struct hwmon_ops lm75_hwmon_ops = { + .is_visible = lm75_is_visible, + .read = lm75_read, + .write = lm75_write, +}; + +static const struct hwmon_chip_info lm75_chip_info = { + .ops = &lm75_hwmon_ops, + .info = lm75_info, +}; + +static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg) +{ + return reg != LM75_REG_TEMP; +} + +static bool lm75_is_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == LM75_REG_TEMP || reg == LM75_REG_CONF; +} + +static const struct regmap_config lm75_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = PCT2075_REG_IDLE, + .writeable_reg = lm75_is_writeable_reg, + .volatile_reg = lm75_is_volatile_reg, + .val_format_endian = REGMAP_ENDIAN_BIG, + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static void lm75_disable_regulator(void *data) +{ + struct lm75_data *lm75 = data; + + regulator_disable(lm75->vs); +} + +static void lm75_remove(void *data) +{ + struct lm75_data *lm75 = data; + struct i2c_client *client = lm75->client; + + i2c_smbus_write_byte_data(client, LM75_REG_CONF, lm75->orig_conf); +} + +static const struct i2c_device_id lm75_ids[]; + +static int lm75_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct lm75_data *data; + int status, err; + enum lm75_type kind; + + if (client->dev.of_node) + kind = (enum lm75_type)of_device_get_match_data(&client->dev); + else + kind = i2c_match_id(lm75_ids, client)->driver_data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + data = devm_kzalloc(dev, sizeof(struct lm75_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + data->kind = kind; + + data->vs = devm_regulator_get(dev, "vs"); + if (IS_ERR(data->vs)) + return PTR_ERR(data->vs); + + data->regmap = devm_regmap_init_i2c(client, &lm75_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + /* Set to LM75 resolution (9 bits, 1/2 degree C) and range. + * Then tweak to be more precise when appropriate. + */ + + data->params = &device_params[data->kind]; + + /* Save default sample time and resolution*/ + data->sample_time = data->params->default_sample_time; + data->resolution = data->params->default_resolution; + + /* Enable the power */ + err = regulator_enable(data->vs); + if (err) { + dev_err(dev, "failed to enable regulator: %d\n", err); + return err; + } + + err = devm_add_action_or_reset(dev, lm75_disable_regulator, data); + if (err) + return err; + + /* Cache original configuration */ + status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); + if (status < 0) { + dev_dbg(dev, "Can't read config? %d\n", status); + return status; + } + data->orig_conf = status; + data->current_conf = status; + + err = lm75_write_config(data, data->params->set_mask, + data->params->clr_mask); + if (err) + return err; + + err = devm_add_action_or_reset(dev, lm75_remove, data); + if (err) + return err; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &lm75_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), client->name); + + return 0; +} + +static const struct i2c_device_id lm75_ids[] = { + { "adt75", adt75, }, + { "at30ts74", at30ts74, }, + { "ds1775", ds1775, }, + { "ds75", ds75, }, + { "ds7505", ds7505, }, + { "g751", g751, }, + { "lm75", lm75, }, + { "lm75a", lm75a, }, + { "lm75b", lm75b, }, + { "max6625", max6625, }, + { "max6626", max6626, }, + { "max31725", max31725, }, + { "max31726", max31725, }, + { "mcp980x", mcp980x, }, + { "pct2075", pct2075, }, + { "stds75", stds75, }, + { "stlm75", stlm75, }, + { "tcn75", tcn75, }, + { "tmp100", tmp100, }, + { "tmp101", tmp101, }, + { "tmp105", tmp105, }, + { "tmp112", tmp112, }, + { "tmp175", tmp175, }, + { "tmp275", tmp275, }, + { "tmp75", tmp75, }, + { "tmp75b", tmp75b, }, + { "tmp75c", tmp75c, }, + { "tmp1075", tmp1075, }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, lm75_ids); + +static const struct of_device_id __maybe_unused lm75_of_match[] = { + { + .compatible = "adi,adt75", + .data = (void *)adt75 + }, + { + .compatible = "atmel,at30ts74", + .data = (void *)at30ts74 + }, + { + .compatible = "dallas,ds1775", + .data = (void *)ds1775 + }, + { + .compatible = "dallas,ds75", + .data = (void *)ds75 + }, + { + .compatible = "dallas,ds7505", + .data = (void *)ds7505 + }, + { + .compatible = "gmt,g751", + .data = (void *)g751 + }, + { + .compatible = "national,lm75", + .data = (void *)lm75 + }, + { + .compatible = "national,lm75a", + .data = (void *)lm75a + }, + { + .compatible = "national,lm75b", + .data = (void *)lm75b + }, + { + .compatible = "maxim,max6625", + .data = (void *)max6625 + }, + { + .compatible = "maxim,max6626", + .data = (void *)max6626 + }, + { + .compatible = "maxim,max31725", + .data = (void *)max31725 + }, + { + .compatible = "maxim,max31726", + .data = (void *)max31725 + }, + { + .compatible = "maxim,mcp980x", + .data = (void *)mcp980x + }, + { + .compatible = "nxp,pct2075", + .data = (void *)pct2075 + }, + { + .compatible = "st,stds75", + .data = (void *)stds75 + }, + { + .compatible = "st,stlm75", + .data = (void *)stlm75 + }, + { + .compatible = "microchip,tcn75", + .data = (void *)tcn75 + }, + { + .compatible = "ti,tmp100", + .data = (void *)tmp100 + }, + { + .compatible = "ti,tmp101", + .data = (void *)tmp101 + }, + { + .compatible = "ti,tmp105", + .data = (void *)tmp105 + }, + { + .compatible = "ti,tmp112", + .data = (void *)tmp112 + }, + { + .compatible = "ti,tmp175", + .data = (void *)tmp175 + }, + { + .compatible = "ti,tmp275", + .data = (void *)tmp275 + }, + { + .compatible = "ti,tmp75", + .data = (void *)tmp75 + }, + { + .compatible = "ti,tmp75b", + .data = (void *)tmp75b + }, + { + .compatible = "ti,tmp75c", + .data = (void *)tmp75c + }, + { + .compatible = "ti,tmp1075", + .data = (void *)tmp1075 + }, + { }, +}; +MODULE_DEVICE_TABLE(of, lm75_of_match); + +#define LM75A_ID 0xA1 + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm75_detect(struct i2c_client *new_client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = new_client->adapter; + int i; + int conf, hyst, os; + bool is_lm75a = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + /* + * Now, we do the remaining detection. There is no identification- + * dedicated register so we have to rely on several tricks: + * unused bits, registers cycling over 8-address boundaries, + * addresses 0x04-0x07 returning the last read value. + * The cycling+unused addresses combination is not tested, + * since it would significantly slow the detection down and would + * hardly add any value. + * + * The National Semiconductor LM75A is different than earlier + * LM75s. It has an ID byte of 0xaX (where X is the chip + * revision, with 1 being the only revision in existence) in + * register 7, and unused registers return 0xff rather than the + * last read value. + * + * Note that this function only detects the original National + * Semiconductor LM75 and the LM75A. Clones from other vendors + * aren't detected, on purpose, because they are typically never + * found on PC hardware. They are found on embedded designs where + * they can be instantiated explicitly so detection is not needed. + * The absence of identification registers on all these clones + * would make their exhaustive detection very difficult and weak, + * and odds are that the driver would bind to unsupported devices. + */ + + /* Unused bits */ + conf = i2c_smbus_read_byte_data(new_client, 1); + if (conf & 0xe0) + return -ENODEV; + + /* First check for LM75A */ + if (i2c_smbus_read_byte_data(new_client, 7) == LM75A_ID) { + /* + * LM75A returns 0xff on unused registers so + * just to be sure we check for that too. + */ + if (i2c_smbus_read_byte_data(new_client, 4) != 0xff + || i2c_smbus_read_byte_data(new_client, 5) != 0xff + || i2c_smbus_read_byte_data(new_client, 6) != 0xff) + return -ENODEV; + is_lm75a = 1; + hyst = i2c_smbus_read_byte_data(new_client, 2); + os = i2c_smbus_read_byte_data(new_client, 3); + } else { /* Traditional style LM75 detection */ + /* Unused addresses */ + hyst = i2c_smbus_read_byte_data(new_client, 2); + if (i2c_smbus_read_byte_data(new_client, 4) != hyst + || i2c_smbus_read_byte_data(new_client, 5) != hyst + || i2c_smbus_read_byte_data(new_client, 6) != hyst + || i2c_smbus_read_byte_data(new_client, 7) != hyst) + return -ENODEV; + os = i2c_smbus_read_byte_data(new_client, 3); + if (i2c_smbus_read_byte_data(new_client, 4) != os + || i2c_smbus_read_byte_data(new_client, 5) != os + || i2c_smbus_read_byte_data(new_client, 6) != os + || i2c_smbus_read_byte_data(new_client, 7) != os) + return -ENODEV; + } + /* + * It is very unlikely that this is a LM75 if both + * hysteresis and temperature limit registers are 0. + */ + if (hyst == 0 && os == 0) + return -ENODEV; + + /* Addresses cycling */ + for (i = 8; i <= 248; i += 40) { + if (i2c_smbus_read_byte_data(new_client, i + 1) != conf + || i2c_smbus_read_byte_data(new_client, i + 2) != hyst + || i2c_smbus_read_byte_data(new_client, i + 3) != os) + return -ENODEV; + if (is_lm75a && i2c_smbus_read_byte_data(new_client, i + 7) + != LM75A_ID) + return -ENODEV; + } + + strscpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE); + + return 0; +} + +#ifdef CONFIG_PM +static int lm75_suspend(struct device *dev) +{ + int status; + struct i2c_client *client = to_i2c_client(dev); + + status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); + if (status < 0) { + dev_dbg(&client->dev, "Can't read config? %d\n", status); + return status; + } + status = status | LM75_SHUTDOWN; + i2c_smbus_write_byte_data(client, LM75_REG_CONF, status); + return 0; +} + +static int lm75_resume(struct device *dev) +{ + int status; + struct i2c_client *client = to_i2c_client(dev); + + status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); + if (status < 0) { + dev_dbg(&client->dev, "Can't read config? %d\n", status); + return status; + } + status = status & ~LM75_SHUTDOWN; + i2c_smbus_write_byte_data(client, LM75_REG_CONF, status); + return 0; +} + +static const struct dev_pm_ops lm75_dev_pm_ops = { + .suspend = lm75_suspend, + .resume = lm75_resume, +}; +#define LM75_DEV_PM_OPS (&lm75_dev_pm_ops) +#else +#define LM75_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +static struct i2c_driver lm75_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm75", + .of_match_table = of_match_ptr(lm75_of_match), + .pm = LM75_DEV_PM_OPS, + }, + .probe_new = lm75_probe, + .id_table = lm75_ids, + .detect = lm75_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(lm75_driver); + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("Custom LM75 driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/lm75.h b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/lm75.h new file mode 100644 index 000000000000..b803ada5e3c9 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/lm75.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * lm75.h - Part of lm_sensors, Linux kernel modules for hardware monitoring + * Copyright (c) 2003 Mark M. Hoffman + */ + +/* + * This file contains common code for encoding/decoding LM75 type + * temperature readings, which are emulated by many of the chips + * we support. As the user is unlikely to load more than one driver + * which contains this code, we don't worry about the wasted space. + */ + +#include +#include + +/* straight from the datasheet */ +#define LM75_TEMP_MIN (-55000) +#define LM75_TEMP_MAX 125000 +#define LM75_SHUTDOWN 0x01 + +/* + * TEMP: 0.001C/bit (-55C to +125C) + * REG: (0.5C/bit, two's complement) << 7 + */ +static inline u16 LM75_TEMP_TO_REG(long temp) +{ + int ntemp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); + + ntemp += (ntemp < 0 ? -250 : 250); + return (u16)((ntemp / 500) << 7); +} + +static inline int LM75_TEMP_FROM_REG(u16 reg) +{ + /* + * use integer division instead of equivalent right shift to + * guarantee arithmetic shift and preserve the sign + */ + return ((s16)reg / 128) * 500; +} diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/mc24lc64t.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/mc24lc64t.c new file mode 100644 index 000000000000..638d59d653ec --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/mc24lc64t.c @@ -0,0 +1,172 @@ +/* + * mc24lc64t.c - driver for Microchip 24LC64T + * + * Copyright (C) 2017 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define EEPROM_SIZE 8192 //mc24lt64t eeprom size in bytes. + +struct mc24lc64t_data { + struct mutex update_lock; +}; + +static ssize_t mc24lc64t_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = kobj_to_i2c_client(kobj); + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + unsigned long timeout, read_time, i = 0; + int status; + + mutex_lock(&drvdata->update_lock); + + if (i2c_smbus_write_byte_data(client, off>>8, off)) + { + status = -EIO; + goto exit; + } + + msleep(1); + +begin: + + if (i < count) + { + timeout = jiffies + msecs_to_jiffies(25); /* 25 mS timeout*/ + do { + read_time = jiffies; + + status = i2c_smbus_read_byte(client); + if (status >= 0) + { + buf[i++] = status; + goto begin; + } + } while (time_before(read_time, timeout)); + + status = -ETIMEDOUT; + goto exit; + } + + status = count; + +exit: + mutex_unlock(&drvdata->update_lock); + + return status; +} + + +static ssize_t mc24lc64t_write (struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count){ + + struct i2c_client *client = kobj_to_i2c_client(kobj); + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + unsigned long timeout, write_time, i = 0; + int status; + u16 value; + + mutex_lock(&drvdata->update_lock); + +begin: + if (i < count){ + timeout = jiffies + msecs_to_jiffies(25); /* 25 mS timeout*/ + value = (buf[i] << 8 | ( off &0xff)); + do { + write_time = jiffies; + status = i2c_smbus_write_word_data(client, off>>8, value); + if (status >= 0) + { + // increase offset + off++; + // increase buffer index + i++; + goto begin; + } + } while (time_before(write_time, timeout)); + status = -ETIMEDOUT; + goto exit; + } + status = count; + +exit: + mutex_unlock(&drvdata->update_lock); + return status; +} + + +static struct bin_attribute mc24lc64t_bit_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO | S_IWUGO, + }, + .size = EEPROM_SIZE, + .read = mc24lc64t_read, + .write = mc24lc64t_write, +}; + +static int mc24lc64t_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct mc24lc64t_data *drvdata; + int err; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA + | I2C_FUNC_SMBUS_READ_BYTE)) + return -EPFNOSUPPORT; + + if (!(drvdata = devm_kzalloc(&client->dev, + sizeof(struct mc24lc64t_data), GFP_KERNEL))) + return -ENOMEM; + + i2c_set_clientdata(client, drvdata); + mutex_init(&drvdata->update_lock); + + err = sysfs_create_bin_file(&client->dev.kobj, &mc24lc64t_bit_attr); + return err; +} + +static void mc24lc64t_remove(struct i2c_client *client) +{ + struct mc24lc64t_data *drvdata = i2c_get_clientdata(client); + sysfs_remove_bin_file(&client->dev.kobj, &mc24lc64t_bit_attr); +} + +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/ds3000/modules/pddf_custom_fpga_algo.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/pddf_custom_fpga_algo.c new file mode 100644 index 000000000000..2e56d03ab790 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/pddf_custom_fpga_algo.c @@ -0,0 +1,626 @@ +/* + * pddf_custom_fpga_algo.c - driver algorithm for FPGAPCIE AXI IIC. + * + * Copyright (C) 2023 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. + * + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../../pddf/i2c/modules/include/pddf_i2c_algo.h" + +#define DEBUG_KERN 0 + +enum { + STATE_DONE = 0, + STATE_INIT, + STATE_ADDR, + STATE_ADDR10, + STATE_START, + STATE_WRITE, + STATE_READ, + STATE_STOP, + STATE_ERROR, +}; + +#define XIIC_MSB_OFFSET 0 +#define XIIC_REG_OFFSET (0x100 + XIIC_MSB_OFFSET) + +/* + * Register offsets in bytes from RegisterBase. Three is added to the + * base offset to access LSB (IBM style) of the word + */ +#define XIIC_CR_REG_OFFSET (0x00 + XIIC_REG_OFFSET) /* Control Register */ +#define XIIC_SR_REG_OFFSET (0x04 + XIIC_REG_OFFSET) /* Status Register */ +#define XIIC_DTR_REG_OFFSET (0x08 + XIIC_REG_OFFSET) /* Data Tx Register */ +#define XIIC_DRR_REG_OFFSET (0x0C + XIIC_REG_OFFSET) /* Data Rx Register */ +#define XIIC_ADR_REG_OFFSET (0x10 + XIIC_REG_OFFSET) /* Address Register */ +#define XIIC_TFO_REG_OFFSET (0x14 + XIIC_REG_OFFSET) /* Tx FIFO Occupancy */ +#define XIIC_RFO_REG_OFFSET (0x18 + XIIC_REG_OFFSET) /* Rx FIFO Occupancy */ +#define XIIC_TBA_REG_OFFSET (0x1C + XIIC_REG_OFFSET) /* 10 Bit Address reg */ +#define XIIC_RFD_REG_OFFSET (0x20 + XIIC_REG_OFFSET) /* Rx FIFO Depth reg */ +#define XIIC_GPO_REG_OFFSET (0x24 + XIIC_REG_OFFSET) /* Output Register */ + +/* Control Register masks */ +#define XIIC_CR_ENABLE_DEVICE_MASK 0x01 /* Device enable = 1 */ +#define XIIC_CR_TX_FIFO_RESET_MASK 0x02 /* Transmit FIFO reset=1 */ +#define XIIC_CR_MSMS_MASK 0x04 /* Master starts Txing=1 */ +#define XIIC_CR_DIR_IS_TX_MASK 0x08 /* Dir of tx. Txing=1 */ +#define XIIC_CR_NO_ACK_MASK 0x10 /* Tx Ack. NO ack = 1 */ +#define XIIC_CR_REPEATED_START_MASK 0x20 /* Repeated start = 1 */ +#define XIIC_CR_GENERAL_CALL_MASK 0x40 /* Gen Call enabled = 1 */ + +/* Status Register masks */ +#define XIIC_SR_GEN_CALL_MASK 0x01 /* 1=a mstr issued a GC */ +#define XIIC_SR_ADDR_AS_SLAVE_MASK 0x02 /* 1=when addr as slave */ +#define XIIC_SR_BUS_BUSY_MASK 0x04 /* 1 = bus is busy */ +#define XIIC_SR_MSTR_RDING_SLAVE_MASK 0x08 /* 1=Dir: mstr <-- slave */ +#define XIIC_SR_TX_FIFO_FULL_MASK 0x10 /* 1 = Tx FIFO full */ +#define XIIC_SR_RX_FIFO_FULL_MASK 0x20 /* 1 = Rx FIFO full */ +#define XIIC_SR_RX_FIFO_EMPTY_MASK 0x40 /* 1 = Rx FIFO empty */ +#define XIIC_SR_TX_FIFO_EMPTY_MASK 0x80 /* 1 = Tx FIFO empty */ + +/* Interrupt Status Register masks Interrupt occurs when... */ +#define XIIC_INTR_ARB_LOST_MASK 0x01 /* 1 = arbitration lost */ +#define XIIC_INTR_TX_ERROR_MASK 0x02 /* 1=Tx error/msg complete */ +#define XIIC_INTR_TX_EMPTY_MASK 0x04 /* 1 = Tx FIFO/reg empty */ +#define XIIC_INTR_RX_FULL_MASK 0x08 /* 1=Rx FIFO/reg=OCY level */ +#define XIIC_INTR_BNB_MASK 0x10 /* 1 = Bus not busy */ +#define XIIC_INTR_AAS_MASK 0x20 /* 1 = when addr as slave */ +#define XIIC_INTR_NAAS_MASK 0x40 /* 1 = not addr as slave */ +#define XIIC_INTR_TX_HALF_MASK 0x80 /* 1 = TX FIFO half empty */ + +/* The following constants specify the depth of the FIFOs */ +#define IIC_RX_FIFO_DEPTH 16 /* Rx fifo capacity */ +#define IIC_TX_FIFO_DEPTH 16 /* Tx fifo capacity */ + +/* + * Tx Fifo upper bit masks. + */ +#define XIIC_TX_DYN_START_MASK 0x0100 /* 1 = Set dynamic start */ +#define XIIC_TX_DYN_STOP_MASK 0x0200 /* 1 = Set dynamic stop */ + +/* + * The following constants define the register offsets for the Interrupt + * registers. There are some holes in the memory map for reserved addresses + * to allow other registers to be added and still match the memory map of the + * interrupt controller registers + */ +#define XIIC_IISR_OFFSET 0x20 /* Interrupt Status Register */ +#define XIIC_RESETR_OFFSET 0x40 /* Reset Register */ + +#define XIIC_RESET_MASK 0xAUL + +#define XIIC_PM_TIMEOUT 1000 /* ms */ +/* timeout waiting for the controller to respond */ +#define XIIC_I2C_TIMEOUT (msecs_to_jiffies(1000)) + +struct fpgalogic_i2c { + void __iomem *base; + u32 reg_shift; + u32 reg_io_width; + wait_queue_head_t wait; + struct i2c_msg *msg; + int pos; + int nmsgs; + int state; /* see STATE_ */ + int ip_clock_khz; + int bus_clock_khz; + void (*reg_set)(struct fpgalogic_i2c *i2c, int reg, u8 value); + u8 (*reg_get)(struct fpgalogic_i2c *i2c, int reg); + u32 timeout; + struct mutex lock; +}; + +static struct fpgalogic_i2c fpgalogic_i2c[I2C_PCI_MAX_BUS]; +extern void __iomem * fpga_ctl_addr; +extern int (*ptr_fpgapci_read)(uint32_t); +extern int (*ptr_fpgapci_write)(uint32_t, uint32_t); +extern int (*pddf_i2c_pci_add_numbered_bus)(struct i2c_adapter *, int); +static int xiic_reinit(struct fpgalogic_i2c *i2c); + + +void i2c_get_mutex(struct fpgalogic_i2c *i2c) +{ + mutex_lock(&i2c->lock); +} + +/** + * i2c_release_mutex - release mutex + */ +void i2c_release_mutex(struct fpgalogic_i2c *i2c) +{ + mutex_unlock(&i2c->lock); +} + +static inline void xiic_setreg32(struct fpgalogic_i2c *i2c, int reg, int value) +{ + (void)iowrite32(value, i2c->base + reg); +} + +static inline int xiic_getreg32(struct fpgalogic_i2c *i2c, int reg) +{ + u32 ret; + + ret = ioread32(i2c->base + reg); + + return ret; +} + +static inline void xiic_irq_clr(struct fpgalogic_i2c *i2c, u32 mask) +{ + u32 isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET); + + xiic_setreg32(i2c, XIIC_IISR_OFFSET, isr & mask); +} + +static int xiic_clear_rx_fifo(struct fpgalogic_i2c *i2c) +{ + u8 sr; + unsigned long timeout; + + timeout = jiffies + XIIC_I2C_TIMEOUT; + for (sr = xiic_getreg32(i2c, XIIC_SR_REG_OFFSET); + !(sr & XIIC_SR_RX_FIFO_EMPTY_MASK); + sr = xiic_getreg32(i2c, XIIC_SR_REG_OFFSET)) { + xiic_getreg32(i2c, XIIC_DRR_REG_OFFSET); + if (time_after(jiffies, timeout)) { + printk("Failed to clear rx fifo\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +/** + * Wait until something change in a given register + * @i2c: AXI IIC device instance + * @reg: register to query + * @mask: bitmask to apply on register value + * @val: expected result + * @timeout: timeout in jiffies + * + * Timeout is necessary to avoid to stay here forever when the chip + * does not answer correctly. + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int poll_wait(struct fpgalogic_i2c *i2c, + int reg, u8 mask, u8 val, + const unsigned long timeout) +{ + unsigned long j; + u8 status = 0; + + j = jiffies + timeout; + while (1) { + mutex_lock(&i2c->lock); + status = xiic_getreg32(i2c, reg); + mutex_unlock(&i2c->lock); + if ((status & mask) == val) + break; + if (time_after(jiffies, j)) + return -ETIMEDOUT; + cpu_relax(); + cond_resched(); + } + return 0; +} + +/** + * Wait until is possible to process some data + * @i2c: AXI IIC device instance + * + * Used when the device is in polling mode (interrupts disabled). + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int xiic_poll_wait(struct fpgalogic_i2c *i2c) +{ + u8 mask = 0, status = 0; + int err = 0; + int val = 0; + int tmp = 0; + mutex_lock(&i2c->lock); + if (i2c->state == STATE_DONE) { + /* transfer is over */ + mask = XIIC_SR_BUS_BUSY_MASK; + } else if (i2c->state == STATE_WRITE || i2c->state == STATE_START){ + /* on going transfer */ + if (0 == i2c->msg->len){ + mask = XIIC_INTR_TX_ERROR_MASK; + } else { + mask = XIIC_SR_TX_FIFO_FULL_MASK; + } + } + else if (i2c->state == STATE_READ){ + /* on going receive */ + mask = XIIC_SR_TX_FIFO_EMPTY_MASK | XIIC_SR_RX_FIFO_EMPTY_MASK; + } + mutex_unlock(&i2c->lock); + // printk("Wait for: 0x%x\n", mask); + + /* + * once we are here we expect to get the expected result immediately + * so if after 50ms we timeout then something is broken. + */ + + if (1 == i2c->nmsgs && 0 == i2c->msg->len && i2c->state == STATE_START && !(i2c->msg->flags & I2C_M_RD)) { /* for i2cdetect I2C_SMBUS_QUICK mode*/ + err = poll_wait(i2c, XIIC_IISR_OFFSET, mask, mask, msecs_to_jiffies(50)); + mutex_lock(&i2c->lock); + status = xiic_getreg32(i2c, XIIC_SR_REG_OFFSET); + mutex_unlock(&i2c->lock); + if (0 != err) { /* AXI IIC as an transceiver , if ever an XIIC_INTR_TX_ERROR_MASK interrupt happens, means no such i2c device */ + err = 0; + } else { + err = -ETIMEDOUT; + } + } else { + if (mask & XIIC_SR_TX_FIFO_EMPTY_MASK){ + err = poll_wait(i2c, XIIC_SR_REG_OFFSET, mask, XIIC_SR_TX_FIFO_EMPTY_MASK, msecs_to_jiffies(50)); + mask &= ~XIIC_SR_TX_FIFO_EMPTY_MASK; + } + if (0 == err){ + err = poll_wait(i2c, XIIC_SR_REG_OFFSET, mask, 0, msecs_to_jiffies(50)); + } + mutex_lock(&i2c->lock); + status = xiic_getreg32(i2c, XIIC_IISR_OFFSET); + + if ((status & XIIC_INTR_ARB_LOST_MASK) || + ((status & XIIC_INTR_TX_ERROR_MASK) && + !(status & XIIC_INTR_RX_FULL_MASK) && + !(i2c->msg->flags & I2C_M_RD))) { /* AXI IIC as an transceiver , if ever an XIIC_INTR_TX_ERROR_MASK interrupt happens, return */ + err = -ETIMEDOUT; + + if (status & XIIC_INTR_ARB_LOST_MASK) { + val = xiic_getreg32(i2c, XIIC_CR_REG_OFFSET); + tmp = XIIC_CR_MSMS_MASK; + val &=(~tmp); + xiic_setreg32(i2c, XIIC_CR_REG_OFFSET, val); + xiic_setreg32(i2c, XIIC_IISR_OFFSET, XIIC_INTR_ARB_LOST_MASK); + printk("%s: TRANSFER STATUS ERROR, ISR: bit 0x%x happens\n", + __func__, XIIC_INTR_ARB_LOST_MASK); + } + if (status & XIIC_INTR_TX_ERROR_MASK) { + int sta = 0; + int cr = 0; + sta = xiic_getreg32(i2c,XIIC_SR_REG_OFFSET); + cr = xiic_getreg32(i2c,XIIC_CR_REG_OFFSET); + xiic_setreg32(i2c, XIIC_IISR_OFFSET, XIIC_INTR_TX_ERROR_MASK); + printk("%s: TRANSFER STATUS ERROR, ISR: bit 0x%x happens; SR: bit 0x%x; CR: bit 0x%x\n", + __func__, status, sta, cr); + } + /* Soft reset IIC controller. */ + xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK); + (void)xiic_reinit(i2c); + mutex_unlock(&i2c->lock); + return err; + } + mutex_unlock(&i2c->lock); + } + + if (err) + printk("%s: STATUS timeout, bit 0x%x did not clear in 50ms\n", + __func__, status); + return err; +} + +static void xiic_process(struct fpgalogic_i2c *i2c) +{ + struct i2c_msg *msg = i2c->msg; + //unsigned long flags; + u16 val; + + /* + * If we spin here because we are in timeout, so we are going + * to be in STATE_ERROR. + */ + mutex_lock(&i2c->lock); + // printk("STATE: %d\n", i2c->state); + + if (i2c->state == STATE_START) { + i2c->state =(msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + /* if it's the time sequence is 'start bit + address + read bit + stop bit' */ + if (i2c->state == STATE_READ){ + /* it's the last message so we include dynamic stop bit with length */ + val = msg->len | XIIC_TX_DYN_STOP_MASK; + xiic_setreg32(i2c, XIIC_DTR_REG_OFFSET, val); + goto out; + } + } + if (i2c->state == STATE_READ){ + /* suit for I2C_FUNC_SMBUS_BLOCK_DATA */ + if (msg->flags & I2C_M_RECV_LEN) { + msg->len = xiic_getreg32(i2c, XIIC_DRR_REG_OFFSET); + msg->flags &= ~I2C_M_RECV_LEN; + msg->buf[i2c->pos++] = msg->len; + } + else { + msg->buf[i2c->pos++] = xiic_getreg32(i2c, XIIC_DRR_REG_OFFSET); + } + } else if (i2c->state == STATE_WRITE){ + /* if it reaches the last byte data to be sent */ + if ((i2c->pos == msg->len - 1) && (i2c->nmsgs == 1)){ + val = msg->buf[i2c->pos++] | XIIC_TX_DYN_STOP_MASK; + xiic_setreg32(i2c, XIIC_DTR_REG_OFFSET, val); + i2c->state = STATE_DONE; + goto out; + /* if it is not the last byte data to be sent */ + } else if (i2c->pos < msg->len) { + xiic_setreg32(i2c, XIIC_DTR_REG_OFFSET, msg->buf[i2c->pos++]); + goto out; + } + } + + /* end of msg? */ + if (i2c->pos == msg->len) { + i2c->nmsgs--; + i2c->pos = 0; + if (i2c->nmsgs) { + i2c->msg++; + msg = i2c->msg; + if (!(msg->flags & I2C_M_NOSTART)) /* send start? */{ + i2c->state = STATE_START; + xiic_setreg32(i2c, XIIC_DTR_REG_OFFSET, i2c_8bit_addr_from_msg(msg) | XIIC_TX_DYN_START_MASK); + goto out; + } + } else { /* end? */ + i2c->state = STATE_DONE; + goto out; + } + } + +out: + mutex_unlock(&i2c->lock); + return ; +} + +static int fpga_axi_iic_poll(struct fpgalogic_i2c *i2c, + struct i2c_msg *msgs, int num) +{ + int ret = 0; + // u8 ctrl; + + mutex_lock(&i2c->lock); + /* Soft reset IIC controller. */ + xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK); + /* Set receive Fifo depth to maximum (zero based). */ + xiic_setreg32(i2c, XIIC_RFD_REG_OFFSET, IIC_RX_FIFO_DEPTH - 1); + + /* Reset Tx Fifo. */ + xiic_setreg32(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK); + + /* Enable IIC Device, remove Tx Fifo reset & disable general call. */ + xiic_setreg32(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_ENABLE_DEVICE_MASK); + + /* set i2c clock as 100Hz. */ + //xiic_setreg32(i2c, 0x13c, 0x7C); + + /* make sure RX fifo is empty */ + ret = xiic_clear_rx_fifo(i2c); + if (ret){ + mutex_unlock(&i2c->lock); + return ret; + } + + i2c->msg = msgs; + i2c->pos = 0; + i2c->nmsgs = num; + i2c->state = STATE_START; + + // printk("STATE: %d\n", i2c->state); + + if (msgs->len == 0 && num == 1){ /* suit for i2cdetect time sequence */ + u8 status = xiic_getreg32(i2c, XIIC_IISR_OFFSET); + xiic_irq_clr(i2c, status); + /* send out the 1st byte data and stop bit */ + xiic_setreg32(i2c, XIIC_DTR_REG_OFFSET, i2c_8bit_addr_from_msg(msgs) | XIIC_TX_DYN_START_MASK | XIIC_TX_DYN_STOP_MASK); + } else { + /* send out the 1st byte data */ + xiic_setreg32(i2c, XIIC_DTR_REG_OFFSET, i2c_8bit_addr_from_msg(msgs) | XIIC_TX_DYN_START_MASK); + } + mutex_unlock(&i2c->lock); + while (1) { + int err; + + err = xiic_poll_wait(i2c); + if (err) { + i2c->state = STATE_ERROR; + break; + }else if (i2c->state == STATE_DONE){ + break; + } + xiic_process(i2c); + } + + return (i2c->state == STATE_DONE) ? num : -EIO; +} + +static int fpga_axi_iic_access(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct fpgalogic_i2c *i2c = i2c_get_adapdata(adap); + int err = -EIO; + u8 retry = 0, max_retry = 0; + + if(((1 == msgs->len && (msgs->flags & I2C_M_RD)) + || (0 == msgs->len && !(msgs->flags & I2C_M_RD)) ) && num == 1 ) /* I2C_SMBUS_QUICK or I2C_SMBUS_BYTE */ + max_retry = 1; + else + max_retry = 5; // retry 5 times if receive a NACK or other errors + + while((-EIO == err) && (retry < max_retry)) + { + err = fpga_axi_iic_poll(i2c, msgs, num); + retry++; + } + + return err; +} + +/** + * A callback function show available smbus functions. + */ +static u32 fpga_axi_iic_func(struct i2c_adapter *adap) +{ + /* a typical full-I2C adapter would use the following */ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm axi_iic_algorithm = { + .master_xfer = fpga_axi_iic_access, + .functionality = fpga_axi_iic_func, +}; + +static int xiic_reinit(struct fpgalogic_i2c *i2c) +{ + int ret; + int val = 0; + + /* Soft reset IIC controller. */ + xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK); + + /* Set receive Fifo depth to maximum (zero based). */ + xiic_setreg32(i2c, XIIC_RFD_REG_OFFSET, IIC_RX_FIFO_DEPTH - 1); + + /* Reset Tx Fifo. */ + xiic_setreg32(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK); + + /* Enable IIC Device, remove Tx Fifo reset & disable general call. */ + val |= XIIC_CR_ENABLE_DEVICE_MASK; + //val |= XIIC_CR_TX_FIFO_RESET_MASK; + //val |= XIIC_CR_MSMS_MASK; + val |= XIIC_CR_DIR_IS_TX_MASK; + xiic_setreg32(i2c, XIIC_CR_REG_OFFSET, val); + + /* make sure RX fifo is empty */ + ret = xiic_clear_rx_fifo(i2c); + if (ret) + return ret; + + return 0; +} + +static int fpgai2c_init(struct fpgalogic_i2c *i2c) +{ + // int prescale; + // int diff; + // u8 ctrl; + int ret; + + + //i2c->reg_set = xiic_setreg32; + //i2c->reg_get = xiic_getreg32; + + ret = xiic_reinit(i2c); + if (ret < 0) { + printk("Cannot xiic_reinit\n"); + return ret; + } + + /* Initialize interrupt handlers if not already done */ + init_waitqueue_head(&i2c->wait); + return 0; +}; + +static int adap_data_init(struct i2c_adapter *adap, int i2c_ch_index) +{ + struct fpgapci_devdata *pci_privdata = 0; + pci_privdata = (struct fpgapci_devdata*) dev_get_drvdata(adap->dev.parent); + + if (pci_privdata == 0) { + printk("[%s]: ERROR pci_privdata is 0\n", __FUNCTION__); + return -1; + } +#ifdef DEBUG_KERN + pddf_dbg(FPGA, KERN_INFO "[%s] index: [%d] fpga_data__base_addr:0x%0x8lx" + " fpgapci_bar_len:0x%08lx fpga_i2c_ch_base_addr:0x%08lx ch_size=0x%x supported_i2c_ch=%d", + __FUNCTION__, i2c_ch_index, pci_privdata->fpga_data_base_addr, + pci_privdata->bar_length, pci_privdata->fpga_i2c_ch_base_addr, + pci_privdata->fpga_i2c_ch_size, pci_privdata->max_fpga_i2c_ch); +#endif + if (i2c_ch_index >= pci_privdata->max_fpga_i2c_ch + || pci_privdata->max_fpga_i2c_ch > I2C_PCI_MAX_BUS){ + printk("[%s]: ERROR i2c_ch_index=%d max_ch_index=%d out of range: %d\n", + __FUNCTION__, i2c_ch_index, pci_privdata->max_fpga_i2c_ch, I2C_PCI_MAX_BUS); + return -1; + } + +#ifdef __STDC_LIB_EXT1__ + memset_s(&fpgalogic_i2c[i2c_ch_index], sizeof(fpgalogic_i2c[0]), 0, sizeof(fpgalogic_i2c[0])); +#else + memset(&fpgalogic_i2c[i2c_ch_index], 0, sizeof(fpgalogic_i2c[0])); +#endif + fpgalogic_i2c[i2c_ch_index].base = pci_privdata->fpga_i2c_ch_base_addr + + i2c_ch_index* pci_privdata->fpga_i2c_ch_size; + mutex_init(&fpgalogic_i2c[i2c_ch_index].lock); + fpgai2c_init(&fpgalogic_i2c[i2c_ch_index]); + + adap->algo_data = &fpgalogic_i2c[i2c_ch_index]; + i2c_set_adapdata(adap, &fpgalogic_i2c[i2c_ch_index]); + return 0; +} + +static int pddf_i2c_pci_add_numbered_bus_default (struct i2c_adapter *adap, int i2c_ch_index) +{ + int ret = 0; + + adap_data_init(adap, i2c_ch_index); + adap->algo = &axi_iic_algorithm; + ret = i2c_add_numbered_adapter(adap); + return ret; +} + +/* + * FPGAPCI APIs + */ +static int board_i2c_fpgapci_read(uint32_t offset) +{ + int data; + data=ioread32(fpga_ctl_addr+offset); + return data; +} + + +static int board_i2c_fpgapci_write(uint32_t offset, uint32_t value) +{ + iowrite32(value, fpga_ctl_addr+offset); + return (0); +} + + +static int __init pddf_custom_fpga_algo_init(void) +{ + pddf_dbg(FPGA, KERN_INFO "[%s]\n", __FUNCTION__); + pddf_i2c_pci_add_numbered_bus = &pddf_i2c_pci_add_numbered_bus_default; + ptr_fpgapci_read = board_i2c_fpgapci_read; + ptr_fpgapci_write = board_i2c_fpgapci_write; + return 0; +} +static void __exit pddf_custom_fpga_algo_exit(void) +{ + pddf_dbg(FPGA, KERN_INFO "[%s]\n", __FUNCTION__); + pddf_i2c_pci_add_numbered_bus = NULL; + ptr_fpgapci_read = NULL; + ptr_fpgapci_write = NULL; + return; +} + +module_init(pddf_custom_fpga_algo_init); +module_exit(pddf_custom_fpga_algo_exit); + +MODULE_DESCRIPTION("Module driver algorithm for 7021 FPGAPCIe AXI IIC"); +MODULE_VERSION("1.0.0"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/pddf_custom_fpga_extend.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/pddf_custom_fpga_extend.c new file mode 100644 index 000000000000..ecb099829297 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/pddf_custom_fpga_extend.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * fpga-cls.c - front panel port control. + * + * Copyright (C) 2019 Celestica Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../../../../pddf/i2c/modules/include/pddf_i2c_algo.h" + +#define FPGA_VERSION_ADDR 0x0000 +#define FPGA_SCRATCH_ADDR 0x0004 +#define FPGA_BCM_TEMP_ADDR 0x001c +#define FPGA_BCM_TEMP_LOW_ADDR 0x0078 +#define FPGA_BCM_TEMP_HIGH_ADDR 0x0080 +#define FPGA_REG_SPACE_SIZE 0x2000 + + +/* + * fpga_priv - port fpga private data + * @dev: device for reference + * @base: virtual base address + * @num_ports: number of front panel ports + * @fp_devs: list of front panel port devices + */ +struct fpga_priv { + void __iomem *base; + struct mutex fpga_lock; // For FPGA internal lock + void __iomem * fpga_read_addr; +}; + +extern void __iomem * fpga_ctl_addr; + +/** + * 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; + struct fpga_priv *fpga = dev_get_drvdata(dev); + + data = ioread32(fpga->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; + struct fpga_priv *fpga = dev_get_drvdata(dev); + + addr = (uint32_t)strtoul(buf, &last, 16); + if (addr == 0 && buf == last) { + return -EINVAL; + } + fpga->fpga_read_addr = fpga->base + 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) +{ + struct fpga_priv *fpga = dev_get_drvdata(dev); + + return sprintf(buf, "0x%8.8x\n", ioread32(fpga->base + FPGA_SCRATCH_ADDR) & 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; + struct fpga_priv *fpga = dev_get_drvdata(dev); + + data = (uint32_t)strtoul(buf, &last, 16); + if (data == 0 && buf == last) { + return -EINVAL; + } + iowrite32(data, fpga->base + FPGA_SCRATCH_ADDR); + return count; +} + +/** + * Show value of fpga version register + * @param buf register value in hexstring + * @return number of bytes read, or an error code + */ +static ssize_t get_fpga_version(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct fpga_priv *fpga = dev_get_drvdata(dev); + + return sprintf(buf, "0x%8.8x\n", ioread32(fpga->base + FPGA_VERSION_ADDR) & 0xffffffff); +} + + +/** + * 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; + struct fpga_priv *fpga = dev_get_drvdata(dev); + + strcpy(clone, buf); + mutex_lock(&fpga->fpga_lock); + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + mutex_unlock(&fpga->fpga_lock); + return -EINVAL; + } + addr = (uint32_t)strtoul(tok, &last, 16); + if (addr == 0 && tok == last) { + mutex_unlock(&fpga->fpga_lock); + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + mutex_unlock(&fpga->fpga_lock); + return -EINVAL; + } + value = (uint32_t)strtoul(tok, &last, 16); + if (value == 0 && tok == last) { + mutex_unlock(&fpga->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->fpga_lock); + return -EINVAL; + } + } + if (mode == 32) { + iowrite32(value, fpga->base + addr); + } else if (mode == 8) { + iowrite8(value, fpga->base + addr); + } else { + mutex_unlock(&fpga->fpga_lock); + return -EINVAL; + } + mutex_unlock(&fpga->fpga_lock); + return count; +} + +/** + * Read all FPGA register in binary mode. + * @param buf Raw transceivers port startus and control register values + * @return number of bytes read, or an error code + */ +static ssize_t dump_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + unsigned long i = 0; + ssize_t status; + u8 read_reg; + struct device *dev = kobj_to_dev(kobj); + struct fpga_priv *fpga = dev_get_drvdata(dev); + + if ( off + count > FPGA_REG_SPACE_SIZE ) { + return -EINVAL; + } + mutex_lock(&fpga->fpga_lock); + while (i < count) { + read_reg = ioread8(fpga->base + off + i); + buf[i++] = read_reg; + } + status = count; + mutex_unlock(&fpga->fpga_lock); + return status; +} + +/** + * Show value of fpga bcm switch internal temp sensor register calculated by FPGA + * @param buf register value in hexstring + * @return number of bytes read, or an error code + */ +static ssize_t get_fpga_bcm_temp_fpga(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct fpga_priv *fpga = dev_get_drvdata(dev); + uint32_t reg_val = ioread32(fpga->base + FPGA_BCM_TEMP_ADDR) & 0x3ffff; + + return sprintf(buf, "0x%08x\n", reg_val); +} + +/** + * Show value of fpga bcm switch internal temp sensor register + * @param buf register value in hexstring + * @return number of bytes read, or an error code + */ +static ssize_t get_fpga_bcm_temp(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct fpga_priv *fpga = dev_get_drvdata(dev); + u8 low_byte = ioread32(fpga->base + FPGA_BCM_TEMP_LOW_ADDR) & 0xff; + u8 high_byte = ioread32(fpga->base + FPGA_BCM_TEMP_HIGH_ADDR) & 0xff; + + return sprintf(buf, "0x%02x%02x\n", high_byte, low_byte); +} + +/* FPGA attributes */ +static DEVICE_ATTR( getreg, 0600, get_fpga_reg_value, set_fpga_reg_address); +static DEVICE_ATTR( setreg, 0200, NULL , set_fpga_reg_value); +static DEVICE_ATTR( scratch, 0600, get_fpga_scratch, set_fpga_scratch); +static DEVICE_ATTR( version, 0400, get_fpga_version, NULL); +static DEVICE_ATTR( bcm_temp_fpga, 0400, get_fpga_bcm_temp_fpga, NULL); +static DEVICE_ATTR( bcm_temp, 0400, get_fpga_bcm_temp, NULL); +static BIN_ATTR_RO( dump, FPGA_REG_SPACE_SIZE); + +static struct bin_attribute *fpga_bin_attrs[] = { + &bin_attr_dump, + NULL, +}; + +static struct attribute *fpga_attrs[] = { + &dev_attr_getreg.attr, + &dev_attr_scratch.attr, + &dev_attr_version.attr, + &dev_attr_bcm_temp_fpga.attr, + &dev_attr_bcm_temp.attr, + &dev_attr_setreg.attr, + NULL, +}; + +static struct attribute_group fpga_attr_grp = { + .attrs = fpga_attrs, + .bin_attrs = fpga_bin_attrs, +}; + + +static int cls_fpga_probe(struct platform_device *pdev) +{ + struct fpga_priv *fpga; + int ret = -ENOMEM; + + if (!fpga_ctl_addr){ + printk(KERN_WARNING, "fpga_ctl_addr is null"); + return ret; + } + + fpga = devm_kzalloc(&pdev->dev, sizeof(struct fpga_priv), GFP_KERNEL); + if (!fpga){ + ret = -ENOMEM; + goto err_exit; + } + + mutex_init(&fpga->fpga_lock); + dev_set_drvdata(&pdev->dev, fpga); + fpga->base = fpga_ctl_addr; + + printk("FPGA version: 0x%x\n", ioread32(fpga->base + FPGA_VERSION_ADDR)); + + ret = sysfs_create_group(&pdev->dev.kobj, &fpga_attr_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create FPGA system sysfs attributes\n"); + goto err_remove_fpga; + } + + return 0; + +err_remove_fpga: + sysfs_remove_group(&pdev->dev.kobj, &fpga_attr_grp); +mem_unmap: + iounmap(fpga->base); +err_exit: + return ret; +} + +static int cls_fpga_remove(struct platform_device *pdev) +{ + struct fpga_priv *fpga = dev_get_drvdata(&pdev->dev); + + sysfs_remove_group(&pdev->dev.kobj, &fpga_attr_grp); + iounmap(fpga->base); + return 0; +} + +static void fpga_dev_release( struct device * dev) +{ + return; +} +static struct resource cls_fpga_resources[] = { + { + .start = NULL, + .end = NULL, + .flags = IORESOURCE_IO, + }, +}; + +static struct platform_device cls_fpga_dev = { + .name = "fpga_sysfs", + .id = -1, + .num_resources = ARRAY_SIZE(cls_fpga_resources), + .resource = cls_fpga_resources, + .dev = { + .release = fpga_dev_release, + } +}; + +static struct platform_driver cls_fpga_driver = { + .probe = cls_fpga_probe, + .remove = cls_fpga_remove, + .driver = { + .name = "fpga_sysfs", + }, +}; + +static int __init drv_init(void) +{ + int rc = 0; + + rc = platform_device_register(&cls_fpga_dev); + rc += platform_driver_register(&cls_fpga_driver); + printk("fpga drv_init:%d\n", rc); + return rc; +} + +static void __exit drv_exit(void) +{ + platform_driver_unregister(&cls_fpga_driver); + platform_device_unregister(&cls_fpga_dev); + printk("fpga drv_exit.\n"); +} + +module_init(drv_init); +module_exit(drv_exit); + +MODULE_AUTHOR("Nicholas Wu"); +MODULE_DESCRIPTION("Celestica fpga access/control driver"); +MODULE_VERSION("2.0.0"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cls-fpga"); diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_api.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_api.c new file mode 100644 index 000000000000..5b7897285571 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_api.c @@ -0,0 +1,478 @@ +/* + * Copyright 2019 Broadcom. + * The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + * 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. + * + * + * Description of various APIs related to PSU component + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../../../pddf/i2c/modules/include/pddf_psu_defs.h" +#include "pddf_psu_driver.h" + + +#define PSU_REG_VOUT_MODE 0x20 +#define PSU_REG_READ_VOUT 0x8b + +/*#define PSU_DEBUG*/ +#ifdef PSU_DEBUG +#define psu_dbg(...) printk(__VA_ARGS__) +#else +#define psu_dbg(...) +#endif + + +void get_psu_duplicate_sysfs(int idx, char *str) +{ + switch (idx) + { + case PSU_V_OUT: + strcpy(str, "in3_input"); + break; + case PSU_I_OUT: + strcpy(str, "curr2_input"); + break; + case PSU_P_OUT: + strcpy(str, "power2_input"); + break; + case PSU_FAN1_SPEED: + strcpy(str, "fan1_input"); + break; + case PSU_TEMP1_INPUT: + strcpy(str, "temp1_input"); + break; + default: + break; + } + + return; +} + +static int two_complement_to_int(u16 data, u8 valid_bit, int mask) +{ + u16 valid_data = data & mask; + bool is_negative = valid_data >> (valid_bit - 1); + + return is_negative ? (-(((~valid_data) & mask) + 1)) : valid_data; +} + +static u8 psu_get_vout_mode(struct i2c_client *client) +{ + u8 status = 0, retry = 10; + uint8_t offset = PSU_REG_VOUT_MODE; + + while (retry) + { + status = i2c_smbus_read_byte_data((struct i2c_client *)client, offset); + if (unlikely(status < 0)) + { + msleep(60); + retry--; + continue; + } + break; + } + + if (status < 0) + { + printk(KERN_ERR "%s: Get PSU Vout mode failed\n", __func__); + return 0; + } + else + { + /*printk(KERN_ERR "%s: vout_mode reg value 0x%x\n", __func__, status);*/ + return status; + } +} + +static u16 psu_get_v_out(struct i2c_client *client) +{ + u16 status = 0, retry = 10; + uint8_t offset = PSU_REG_READ_VOUT; + + while (retry) { + status = i2c_smbus_read_word_data((struct i2c_client *)client, offset); + if (unlikely(status < 0)) { + msleep(60); + retry--; + continue; + } + break; + } + + if (status < 0) + { + printk(KERN_ERR "%s: Get PSU Vout failed\n", __func__); + return 0; + } + else + { + /*printk(KERN_ERR "%s: vout reg value 0x%x\n", __func__, status);*/ + return status; + } +} + +int psu_update_hw(struct device *dev, struct psu_attr_info *info, PSU_DATA_ATTR *udata) +{ + int status = 0; + struct i2c_client *client = to_i2c_client(dev); + PSU_SYSFS_ATTR_DATA *sysfs_attr_data = NULL; + + + mutex_lock(&info->update_lock); + + sysfs_attr_data = udata->access_data; + if (sysfs_attr_data->pre_set != NULL) + { + status = (sysfs_attr_data->pre_set)(client, udata, info); + if (status!=0) + dev_warn(&client->dev, "%s: pre_set function fails for %s attribute. ret %d\n", __FUNCTION__, udata->aname, status); + } + if (sysfs_attr_data->do_set != NULL) + { + status = (sysfs_attr_data->do_set)(client, udata, info); + if (status!=0) + dev_warn(&client->dev, "%s: do_set function fails for %s attribute. ret %d\n", __FUNCTION__, udata->aname, status); + + } + if (sysfs_attr_data->post_set != NULL) + { + status = (sysfs_attr_data->post_set)(client, udata, info); + if (status!=0) + dev_warn(&client->dev, "%s: post_set function fails for %s attribute. ret %d\n", __FUNCTION__, udata->aname, status); + } + + mutex_unlock(&info->update_lock); + + return 0; +} + + +int psu_update_attr(struct device *dev, struct psu_attr_info *data, PSU_DATA_ATTR *udata) +{ + int status = 0; + struct i2c_client *client = to_i2c_client(dev); + PSU_SYSFS_ATTR_DATA *sysfs_attr_data=NULL; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || !data->valid) + { + dev_dbg(&client->dev, "Starting update for %s\n", data->name); + + sysfs_attr_data = udata->access_data; + if (sysfs_attr_data->pre_get != NULL) + { + status = (sysfs_attr_data->pre_get)(client, udata, data); + if (status!=0) + dev_warn(&client->dev, "%s: pre_get function fails for %s attribute. ret %d\n", __FUNCTION__, udata->aname, status); + } + if (sysfs_attr_data->do_get != NULL) + { + status = (sysfs_attr_data->do_get)(client, udata, data); + if (status!=0) + dev_warn(&client->dev, "%s: do_get function fails for %s attribute. ret %d\n", __FUNCTION__, udata->aname, status); + + } + if (sysfs_attr_data->post_get != NULL) + { + status = (sysfs_attr_data->post_get)(client, udata, data); + if (status!=0) + dev_warn(&client->dev, "%s: post_get function fails for %s attribute. ret %d\n", __FUNCTION__, udata->aname, status); + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + return 0; +} + +ssize_t psu_show_default(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct psu_data *data = i2c_get_clientdata(client); + PSU_PDATA *pdata = (PSU_PDATA *)(client->dev.platform_data); + PSU_DATA_ATTR *usr_data = NULL; + struct psu_attr_info *sysfs_attr_info = NULL; + int i, status=0; + u16 value = 0; + u8 vout_mode = 0; + int exponent, mantissa; + int multiplier = 1000; + char new_str[ATTR_NAME_LEN] = ""; + PSU_SYSFS_ATTR_DATA *ptr = NULL; + + for (i=0;inum_attr;i++) + { + ptr = (PSU_SYSFS_ATTR_DATA *)pdata->psu_attrs[i].access_data; + get_psu_duplicate_sysfs(ptr->index , new_str); + if ( strcmp(attr->dev_attr.attr.name, pdata->psu_attrs[i].aname) == 0 || strcmp(attr->dev_attr.attr.name, new_str) == 0 ) + { + sysfs_attr_info = &data->attr_info[i]; + usr_data = &pdata->psu_attrs[i]; + strcpy(new_str, ""); + } + } + + if (sysfs_attr_info==NULL || usr_data==NULL) + { + printk(KERN_ERR "%s is not supported attribute for this client\n", attr->dev_attr.attr.name); + goto exit; + } + + psu_update_attr(dev, sysfs_attr_info, usr_data); + + switch(attr->index) + { + case PSU_PRESENT: + case PSU_POWER_GOOD: + status = sysfs_attr_info->val.intval; + return sprintf(buf, "%d\n", status); + break; + case PSU_MODEL_NAME: + case PSU_MFR_ID: + case PSU_SERIAL_NUM: + case PSU_FAN_DIR: + return sprintf(buf, "%s\n", sysfs_attr_info->val.strval); + break; + case PSU_V_OUT: + value = psu_get_v_out(client); + vout_mode = psu_get_vout_mode(client); + if ((vout_mode >> 5) == 0) + exponent = two_complement_to_int(vout_mode & 0x1f, 5, 0x1f); + else + exponent = 0; + + mantissa = value; + if (exponent >= 0) + return sprintf(buf, "%d\n", (mantissa << exponent) * multiplier); + + else + return sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); + break; + + case PSU_V_OUT_MIN: + case PSU_V_OUT_MAX: + multiplier = 1000; + value = sysfs_attr_info->val.shortval; + vout_mode = psu_get_vout_mode(client); + if ((vout_mode >> 5) == 0) + exponent = two_complement_to_int(vout_mode & 0x1f, 5, 0x1f); + else + exponent = 0; + mantissa = two_complement_to_int(value & 0xffff, 16, 0xffff); + + if (exponent >= 0) + return sprintf(buf, "%d\n", (mantissa << exponent) * multiplier); + else + return sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); + break; + case PSU_I_OUT: + case PSU_V_IN: + case PSU_I_IN: + case PSU_P_OUT_MAX: + multiplier = 1000; + value = sysfs_attr_info->val.shortval; + exponent = two_complement_to_int(value >> 11, 5, 0x1f); + mantissa = two_complement_to_int(value & 0x7ff, 11, 0x7ff); + if (exponent >= 0) + return sprintf(buf, "%d\n", (mantissa << exponent) * multiplier); + else + return sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); + break; + case PSU_P_IN: + case PSU_P_OUT: + multiplier = 1000000; + value = sysfs_attr_info->val.shortval; + exponent = two_complement_to_int(value >> 11, 5, 0x1f); + mantissa = two_complement_to_int(value & 0x7ff, 11, 0x7ff); + if (exponent >= 0) + return sprintf(buf, "%d\n", (mantissa << exponent) * multiplier); + else + return sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); + + break; + case PSU_FAN1_SPEED: + value = sysfs_attr_info->val.shortval; + exponent = two_complement_to_int(value >> 11, 5, 0x1f); + mantissa = two_complement_to_int(value & 0x7ff, 11, 0x7ff); + if (exponent >= 0) + return sprintf(buf, "%d\n", (mantissa << exponent)); + else + return sprintf(buf, "%d\n", (mantissa) / (1 << -exponent)); + + break; + case PSU_TEMP1_INPUT: + case PSU_TEMP1_HIGH_THRESHOLD: + multiplier = 1000; + value = sysfs_attr_info->val.shortval; + exponent = two_complement_to_int(value >> 11, 5, 0x1f); + mantissa = two_complement_to_int(value & 0x7ff, 11, 0x7ff); + if (exponent >= 0) + return sprintf(buf, "%d\n", (mantissa << exponent) * multiplier); + else + return sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); + + break; + default: + printk(KERN_ERR "%s: Unable to find attribute index for %s\n", __FUNCTION__, usr_data->aname); + goto exit; + } + +exit: + return sprintf(buf, "%d\n", status); +} + + +ssize_t psu_store_default(struct device *dev, struct device_attribute *da, const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct psu_data *data = i2c_get_clientdata(client); + PSU_PDATA *pdata = (PSU_PDATA *)(client->dev.platform_data); + PSU_DATA_ATTR *usr_data = NULL; + struct psu_attr_info *sysfs_attr_info = NULL; + int i; + + for (i=0;inum_attr;i++) + { + if (strcmp(data->attr_info[i].name, attr->dev_attr.attr.name) == 0 && strcmp(pdata->psu_attrs[i].aname, attr->dev_attr.attr.name) == 0) + { + sysfs_attr_info = &data->attr_info[i]; + usr_data = &pdata->psu_attrs[i]; + } + } + + if (sysfs_attr_info==NULL || usr_data==NULL) { + printk(KERN_ERR "%s is not supported attribute for this client\n", attr->dev_attr.attr.name); + goto exit; + } + + switch(attr->index) + { + /*No write attributes for now in PSU*/ + default: + goto exit; + } + + psu_update_hw(dev, sysfs_attr_info, usr_data); + +exit: + return count; +} + +extern int board_i2c_cpld_read_new(unsigned short cpld_addr, char *name, u8 reg); +int sonic_i2c_get_psu_byte_default(void *client, PSU_DATA_ATTR *adata, void *data) +{ + int status = 0; + int val = 0; + struct psu_attr_info *padata = (struct psu_attr_info *)data; + + + if (strncmp(adata->devtype, "cpld", strlen("cpld")) == 0) + { + val = board_i2c_cpld_read_new(adata->devaddr, adata->devname, adata->offset); + if (val < 0){ + return val; + } + padata->val.intval = ((val & adata->mask) == adata->cmpval); + psu_dbg(KERN_ERR "%s: byte_value = 0x%x\n", __FUNCTION__, padata->val.intval); + } + + return status; +} + +int sonic_i2c_get_psu_block_default(void *client, PSU_DATA_ATTR *adata, void *data) +{ + int status = 0, retry = 10; + struct psu_attr_info *padata = (struct psu_attr_info *)data; + char buf[32]=""; //temporary placeholder for block data + uint8_t offset = (uint8_t)adata->offset; + int data_len = adata->len; + + while (retry) + { + status = i2c_smbus_read_i2c_block_data((struct i2c_client *)client, offset, data_len-1, buf); + if (unlikely(status<0)) + { + msleep(60); + retry--; + continue; + } + break; + } + + if (status < 0) + { + buf[0] = '\0'; + dev_dbg(&((struct i2c_client *)client)->dev, "unable to read block of data from (0x%x)\n", ((struct i2c_client *)client)->addr); + } + else + { + buf[data_len-1] = '\0'; + } + + if (strncmp(adata->devtype, "pmbus", strlen("pmbus")) == 0) + strncpy(padata->val.strval, buf+1, data_len-1); + else + strncpy(padata->val.strval, buf, data_len); + + psu_dbg(KERN_ERR "%s: status = %d, buf block: %s\n", __FUNCTION__, status, padata->val.strval); + return 0; +} + +int sonic_i2c_get_psu_word_default(void *client, PSU_DATA_ATTR *adata, void *data) +{ + + int status = 0, retry = 10; + struct psu_attr_info *padata = (struct psu_attr_info *)data; + uint8_t offset = (uint8_t)adata->offset; + + while (retry) { + status = i2c_smbus_read_word_data((struct i2c_client *)client, offset); + if (unlikely(status < 0)) { + msleep(60); + retry--; + continue; + } + break; + } + + if (status < 0) + { + padata->val.shortval = 0; + dev_dbg(&((struct i2c_client *)client)->dev, "unable to read a word from (0x%x)\n", ((struct i2c_client *)client)->addr); + } + else + { + padata->val.shortval = status; + } + + psu_dbg(KERN_ERR "%s: word value : %d\n", __FUNCTION__, padata->val.shortval); + return 0; +} diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_api.h b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_api.h new file mode 100644 index 000000000000..24e4ea02e7ec --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_api.h @@ -0,0 +1,31 @@ +/* + * Copyright 2019 Broadcom. + * The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + * 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. + * + * + * Description + * PSU driver related api declarations + */ + +#ifndef __PDDF_PSU_API_H__ +#define __PDDF_PSU_API_H__ + +extern void get_psu_duplicate_sysfs(int idx, char *str); +extern ssize_t psu_show_default(struct device *dev, struct device_attribute *da, char *buf); +extern ssize_t psu_store_default(struct device *dev, struct device_attribute *da, const char *buf, size_t count); + +extern int sonic_i2c_get_psu_byte_default(void *client, PSU_DATA_ATTR *adata, void *data); +extern int sonic_i2c_get_psu_block_default(void *client, PSU_DATA_ATTR *adata, void *data); +extern int sonic_i2c_get_psu_word_default(void *client, PSU_DATA_ATTR *adata, void *data); + +#endif diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_defs.h b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_defs.h new file mode 100644 index 000000000000..ab0d61b23c95 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_defs.h @@ -0,0 +1,90 @@ +/* + * Copyright 2019 Broadcom. + * The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + * 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. + * + * + * Description: + * Platform PSU defines/structures header file + */ + +#ifndef __PDDF_PSU_DEFS_H__ +#define __PDDF_PSU_DEFS_H__ + + +#define MAX_NUM_PSU 5 +#define MAX_PSU_ATTRS 32 +#define ATTR_NAME_LEN 32 +#define STR_ATTR_SIZE 32 +#define DEV_TYPE_LEN 32 + +/* Each client has this additional data + */ + +typedef struct PSU_DATA_ATTR +{ + char aname[ATTR_NAME_LEN]; // attr name, taken from enum psu_sysfs_attributes + char devtype[DEV_TYPE_LEN]; // either a 'eeprom' or 'cpld', or 'pmbus' attribute + char devname[DEV_TYPE_LEN]; // Name of the device from where this sysfs attr is read + uint32_t devaddr; + uint32_t offset; + uint32_t mask; + uint32_t cmpval; + uint32_t len; + void *access_data; + +}PSU_DATA_ATTR; + +typedef struct PSU_SYSFS_ATTR_DATA +{ + int index; + unsigned short mode; + ssize_t (*show)(struct device *dev, struct device_attribute *da, char *buf); + int (*pre_get)(void *client, PSU_DATA_ATTR *adata, void *data); + int (*do_get)(void *client, PSU_DATA_ATTR *adata, void *data); + int (*post_get)(void *client, PSU_DATA_ATTR *adata, void *data); + ssize_t (*store)(struct device *dev, struct device_attribute *da, const char *buf, size_t count); + int (*pre_set)(void *client, PSU_DATA_ATTR *adata, void *data); + int (*do_set)(void *client, PSU_DATA_ATTR *adata, void *data); + int (*post_set)(void *client, PSU_DATA_ATTR *adata, void *data); + void *data; +} PSU_SYSFS_ATTR_DATA; + +typedef struct PSU_SYSFS_ATTR_DATA_ENTRY +{ + char name[ATTR_NAME_LEN]; + PSU_SYSFS_ATTR_DATA *a_ptr; +} PSU_SYSFS_ATTR_DATA_ENTRY; + + +/* PSU CLIENT DATA - PLATFORM DATA FOR PSU CLIENT */ +typedef struct PSU_DATA +{ + int idx; // psu index + int num_psu_fans; + PSU_DATA_ATTR psu_attr; + int len; // no of valid attributes for this psu client + PSU_DATA_ATTR psu_attrs[MAX_PSU_ATTRS]; +}PSU_DATA; + +typedef struct PSU_PDATA +{ + int idx; // psu index + int num_psu_fans; // num of fans supported by the PSU + int len; // no of valid attributes for this psu client + PSU_DATA_ATTR *psu_attrs; +}PSU_PDATA; + +extern int board_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int board_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +#endif diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_driver.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_driver.c new file mode 100644 index 000000000000..1d0fb94f5ffe --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_driver.c @@ -0,0 +1,398 @@ +/* + * Copyright 2019 Broadcom. + * The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + * 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. + * + * + * A pddf kernel module driver for PSU + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../../../pddf/i2c/modules/include/pddf_client_defs.h" +#include "../../../../../pddf/i2c/modules/include/pddf_psu_defs.h" +#include "../../../../../pddf/i2c/modules/include/pddf_psu_driver.h" +#include "../../../../../pddf/i2c/modules/include/pddf_psu_api.h" + + +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +struct pddf_ops_t pddf_psu_ops = { + .pre_init = NULL, + .post_init = NULL, + + .pre_probe = NULL, + .post_probe = NULL, + + .pre_remove = NULL, + .post_remove = NULL, + + .pre_exit = NULL, + .post_exit = NULL, +}; +EXPORT_SYMBOL(pddf_psu_ops); + + +PSU_SYSFS_ATTR_DATA access_psu_present = {PSU_PRESENT, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_byte_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_present); + +PSU_SYSFS_ATTR_DATA access_psu_model_name = {PSU_MODEL_NAME, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_block_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_model_name); + +PSU_SYSFS_ATTR_DATA access_psu_power_good = {PSU_POWER_GOOD, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_byte_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_power_good); + +PSU_SYSFS_ATTR_DATA access_psu_mfr_id = {PSU_MFR_ID, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_block_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_mfr_id); + +PSU_SYSFS_ATTR_DATA access_psu_serial_num = {PSU_SERIAL_NUM, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_block_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_serial_num); + +PSU_SYSFS_ATTR_DATA access_psu_fan_dir = {PSU_FAN_DIR, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_block_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_fan_dir); + +PSU_SYSFS_ATTR_DATA access_psu_v_out = {PSU_V_OUT, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_word_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_v_out); + +PSU_SYSFS_ATTR_DATA access_psu_v_out_min = {PSU_V_OUT_MIN, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_word_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_v_out_min); + +PSU_SYSFS_ATTR_DATA access_psu_v_out_max = {PSU_V_OUT_MAX, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_word_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_v_out_max); + +PSU_SYSFS_ATTR_DATA access_psu_i_out = {PSU_I_OUT, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_word_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_i_out); + +PSU_SYSFS_ATTR_DATA access_psu_p_out = {PSU_P_OUT, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_word_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_p_out); + +PSU_SYSFS_ATTR_DATA access_psu_p_out_max = {PSU_P_OUT_MAX, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_word_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_p_out_max); + +PSU_SYSFS_ATTR_DATA access_psu_fan1_speed_rpm = {PSU_FAN1_SPEED, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_word_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_fan1_speed_rpm); + +PSU_SYSFS_ATTR_DATA access_psu_temp1_input = {PSU_TEMP1_INPUT, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_word_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_temp1_input); + +PSU_SYSFS_ATTR_DATA access_psu_temp1_high_threshold = {PSU_TEMP1_HIGH_THRESHOLD, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_word_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_temp1_high_threshold); + +PSU_SYSFS_ATTR_DATA access_psu_v_in = {PSU_V_IN, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_word_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_v_in); + +PSU_SYSFS_ATTR_DATA access_psu_i_in = {PSU_I_IN, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_word_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_i_in); + +PSU_SYSFS_ATTR_DATA access_psu_p_in = {PSU_P_IN, S_IRUGO, psu_show_default, NULL, sonic_i2c_get_psu_word_default, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(access_psu_p_in); + +PSU_SYSFS_ATTR_DATA_ENTRY psu_sysfs_attr_data_tbl[]= +{ + { "psu_present", &access_psu_present}, + { "psu_model_name", &access_psu_model_name}, + { "psu_power_good" , &access_psu_power_good}, + { "psu_mfr_id" , &access_psu_mfr_id}, + { "psu_serial_num" , &access_psu_serial_num}, + { "psu_fan_dir" , &access_psu_fan_dir}, + { "psu_v_out" , &access_psu_v_out}, + { "psu_v_out_min" , &access_psu_v_out_min}, + { "psu_v_out_max" , &access_psu_v_out_max}, + { "psu_i_out" , &access_psu_i_out}, + { "psu_p_out" , &access_psu_p_out}, + { "psu_p_out_max" , &access_psu_p_out_max}, + { "psu_fan1_speed_rpm" , &access_psu_fan1_speed_rpm}, + { "psu_temp1_input" , &access_psu_temp1_input}, + { "psu_temp1_high_threshold" , &access_psu_temp1_high_threshold}, + { "psu_v_in" , &access_psu_v_in}, + { "psu_i_in" , &access_psu_i_in}, + { "psu_p_in" , &access_psu_p_in} +}; + +void *get_psu_access_data(char *name) +{ + int i=0; + for(i=0; i<(sizeof(psu_sysfs_attr_data_tbl)/sizeof(psu_sysfs_attr_data_tbl[0])); i++) + { + if(strcmp(name, psu_sysfs_attr_data_tbl[i].name) ==0) + { + return &psu_sysfs_attr_data_tbl[i]; + } + } + return NULL; +} +EXPORT_SYMBOL(get_psu_access_data); + + +static int psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct psu_data *data; + int status =0; + int i,num, j=0; + PSU_PDATA *psu_platform_data; + PSU_DATA_ATTR *data_attr; + PSU_SYSFS_ATTR_DATA_ENTRY *sysfs_data_entry; + char new_str[ATTR_NAME_LEN] = ""; + + + if (client == NULL) { + printk("NULL Client.. \n"); + goto exit; + } + + if (pddf_psu_ops.pre_probe) + { + status = (pddf_psu_ops.pre_probe)(client, dev_id); + if (status != 0) + goto exit; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + dev_info(&client->dev, "chip found\n"); + + /* Take control of the platform data */ + psu_platform_data = (PSU_PDATA *)(client->dev.platform_data); + num = psu_platform_data->len; + data->index = psu_platform_data->idx - 1; + data->num_psu_fans = psu_platform_data->num_psu_fans; + data->num_attr = num; + + + + /* Create and Add supported attr in the 'attributes' list */ + for (i=0; ipsu_attrs + i; + sysfs_data_entry = get_psu_access_data(data_attr->aname); + if (sysfs_data_entry == NULL) + { + printk(KERN_ERR "%s: Wrong attribute name provided by user '%s'\n", __FUNCTION__, data_attr->aname); + continue; + } + + dy_ptr = (struct sensor_device_attribute *)kzalloc(sizeof(struct sensor_device_attribute)+ATTR_NAME_LEN, GFP_KERNEL); + dy_ptr->dev_attr.attr.name = (char *)&dy_ptr[1]; + strcpy((char *)dy_ptr->dev_attr.attr.name, data_attr->aname); + dy_ptr->dev_attr.attr.mode = sysfs_data_entry->a_ptr->mode; + dy_ptr->dev_attr.show = sysfs_data_entry->a_ptr->show; + dy_ptr->dev_attr.store = sysfs_data_entry->a_ptr->store; + dy_ptr->index = sysfs_data_entry->a_ptr->index; + + data->psu_attribute_list[i] = &dy_ptr->dev_attr.attr; + strcpy(data->attr_info[i].name, data_attr->aname); + data->attr_info[i].valid = 0; + mutex_init(&data->attr_info[i].update_lock); + + /*Create a duplicate entry*/ + get_psu_duplicate_sysfs(dy_ptr->index, new_str); + if (strcmp(new_str,"")) + { + dy_ptr = (struct sensor_device_attribute *)kzalloc(sizeof(struct sensor_device_attribute)+ATTR_NAME_LEN, GFP_KERNEL); + dy_ptr->dev_attr.attr.name = (char *)&dy_ptr[1]; + strcpy((char *)dy_ptr->dev_attr.attr.name, new_str); + dy_ptr->dev_attr.attr.mode = sysfs_data_entry->a_ptr->mode; + dy_ptr->dev_attr.show = sysfs_data_entry->a_ptr->show; + dy_ptr->dev_attr.store = sysfs_data_entry->a_ptr->store; + dy_ptr->index = sysfs_data_entry->a_ptr->index; + + data->psu_attribute_list[num+j] = &dy_ptr->dev_attr.attr; + j++; + strcpy(new_str,""); + } + } + data->psu_attribute_list[i+j] = NULL; + data->psu_attribute_group.attrs = data->psu_attribute_list; + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &data->psu_attribute_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register_with_groups(&client->dev, client->name, NULL, NULL); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + /* Add a support for post probe function */ + if (pddf_psu_ops.post_probe) + { + status = (pddf_psu_ops.post_probe)(client, dev_id); + if (status != 0) + goto exit_remove; + } + + return 0; + + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &data->psu_attribute_group); +exit_free: + /* Free all the allocated attributes */ + for (i=0;data->psu_attribute_list[i]!=NULL;i++) + { + struct sensor_device_attribute *ptr = (struct sensor_device_attribute *)data->psu_attribute_list[i]; + kfree(ptr); + data->psu_attribute_list[i] = NULL; + pddf_dbg(PSU, KERN_ERR "%s: Freed all the memory allocated for attributes\n", __FUNCTION__); + } + kfree(data); +exit: + return status; +} + +static void psu_remove(struct i2c_client *client) +{ + int i=0, ret = 0; + struct psu_data *data = i2c_get_clientdata(client); + PSU_PDATA *platdata = (PSU_PDATA *)client->dev.platform_data; // use dev_get_platdata() + PSU_DATA_ATTR *platdata_sub = platdata->psu_attrs; + struct sensor_device_attribute *ptr = NULL; + + if (pddf_psu_ops.pre_remove) + { + ret = (pddf_psu_ops.pre_remove)(client); + if (ret!=0) + printk(KERN_ERR "FAN pre_remove function failed\n"); + } + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &data->psu_attribute_group); + for (i=0; data->psu_attribute_list[i]!=NULL; i++) + { + ptr = (struct sensor_device_attribute *)data->psu_attribute_list[i]; + kfree(ptr); + data->psu_attribute_list[i] = NULL; + } + pddf_dbg(PSU, KERN_ERR "%s: Freed all the memory allocated for attributes\n", __FUNCTION__); + kfree(data); + if (platdata_sub) { + printk(KERN_DEBUG "%s: Freeing platform subdata\n", __FUNCTION__); + kfree(platdata_sub); + } + if (platdata) { + printk(KERN_DEBUG "%s: Freeing platform data\n", __FUNCTION__); + kfree(platdata); + } + + if (pddf_psu_ops.post_remove) + { + ret = (pddf_psu_ops.post_remove)(client); + if (ret!=0) + printk(KERN_ERR "FAN post_remove function failed\n"); + } +} + +enum psu_intf +{ + eeprom_intf, + smbus_intf +}; + +static const struct i2c_device_id psu_id[] = { + {"psu_eeprom", eeprom_intf}, + {"psu_pmbus", smbus_intf}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, psu_id); + +static struct i2c_driver psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "psu", + }, + .probe = psu_probe, + .remove = psu_remove, + .id_table = psu_id, + .address_list = normal_i2c, +}; + +int example_fun(void) +{ + pddf_dbg(PSU, KERN_ERR "CALLING FUN...\n"); + return 0; +} +EXPORT_SYMBOL(example_fun); + + +int psu_init(void) +{ + int status = 0; + + if (pddf_psu_ops.pre_init) + { + status = (pddf_psu_ops.pre_init)(); + if (status!=0) + return status; + } + + pddf_dbg(PSU, KERN_ERR "GENERIC_PSU_DRIVER.. init Invoked..\n"); + status = i2c_add_driver(&psu_driver); + if (status!=0) + return status; + + if (pddf_psu_ops.post_init) + { + status = (pddf_psu_ops.post_init)(); + if (status!=0) + return status; + } + + return status; +} +EXPORT_SYMBOL(psu_init); + +void psu_exit(void) +{ + pddf_dbg(PSU, "GENERIC_PSU_DRIVER.. exit\n"); + if (pddf_psu_ops.pre_exit) (pddf_psu_ops.pre_exit)(); + i2c_del_driver(&psu_driver); + if (pddf_psu_ops.post_exit) (pddf_psu_ops.post_exit)(); +} +EXPORT_SYMBOL(psu_exit); + +module_init(psu_init); +module_exit(psu_exit); + +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("psu driver"); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_driver.h b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_driver.h new file mode 100644 index 000000000000..a94cf7441dbc --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_driver.h @@ -0,0 +1,70 @@ +/* + * Copyright 2019 Broadcom. + * The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + * 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. + * + * + * Description + * PSU driver data structures + */ +#ifndef __PDDF_PSU_DRIVER_H__ +#define __PDDF_PSU_DRIVER_H__ + +enum psu_sysfs_attributes { + PSU_PRESENT, + PSU_MODEL_NAME, + PSU_POWER_GOOD, + PSU_MFR_ID, + PSU_SERIAL_NUM, + PSU_FAN_DIR, + PSU_V_OUT, + PSU_V_OUT_MIN, + PSU_V_OUT_MAX, + PSU_I_OUT, + PSU_P_OUT, /* This is in micro watts to comply with lm-sensors */ + PSU_P_OUT_MAX, + PSU_FAN1_SPEED, + PSU_TEMP1_INPUT, + PSU_TEMP1_HIGH_THRESHOLD, + PSU_V_IN, + PSU_I_IN, + PSU_P_IN, + PSU_ATTR_MAX +}; + + +/* Every client has psu_data which is divided into per attribute data */ +struct psu_attr_info { + char name[ATTR_NAME_LEN]; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 status; + union { + char strval[STR_ATTR_SIZE]; + int intval; + u16 shortval; + u8 charval; + }val; +}; +struct psu_data { + struct device *hwmon_dev; + u8 index; + int num_psu_fans; + int num_attr; + struct attribute *psu_attribute_list[MAX_PSU_ATTRS]; + struct attribute_group psu_attribute_group; + struct psu_attr_info attr_info[MAX_PSU_ATTRS]; +}; + + +#endif diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/switchboard_fpga.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/switchboard_fpga.c new file mode 100644 index 000000000000..305253403625 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/switchboard_fpga.c @@ -0,0 +1,2365 @@ +/* + * switchboard_fpga.c - driver for ds3000 Switch board FPGA/CPLD. + * + * Author: Pradchaya Phucharoen + * + * Copyright (C) 2019 Celestica Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * / + * \--sys + * \--devices + * \--platform + * \--switchboard + * |--FPGA + * |--CPLD1 + * |--CPLD2 + * \--SFF + * |--QSFP[1..32] + * \--SFP1 + * + */ + +#ifndef TEST_MODE +#define MOD_VERSION "2.2.0" +#else +#define MOD_VERSION "TEST" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int majorNumber; + +#define CLASS_NAME "ds3000_fpga" +#define DRIVER_NAME "switchboard" +#define FPGA_PCI_NAME "ds3000_fpga_pci" +#define DEVICE_NAME "fwupgrade" + + +static int smbus_access(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data); + +static int fpga_i2c_access(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data); + +static int fpgafw_init(void); +static void fpgafw_exit(void); + +/* +======================================== +FPGA PCIe BAR 0 Registers +======================================== +Misc Control 0x00000000 – 0x000000FF. +I2C_CH1 0x00000100 - 0x00000110 +I2C_CH2 0x00000200 - 0x00000210. +I2C_CH3 0x00000300 - 0x00000310. +I2C_CH4 0x00000400 - 0x00000410. +I2C_CH5 0x00000500 - 0x00000510. +I2C_CH6 0x00000600 - 0x00000610. +I2C_CH7 0x00000700 - 0x00000710. +I2C_CH8 0x00000800 - 0x00000810. +I2C_CH9 0x00000900 - 0x00000910. +I2C_CH10 0x00000A00 - 0x00000A10. +SPI Master 0x00001200 - 0x00001300. +PORT XCVR 0x00004000 - 0x00004FFF. +*/ + +/* MISC */ +#define FPGA_VERSION 0x0000 +#define FPGA_VERSION_MJ_MSK 0xff00 +#define FPGA_VERSION_MN_MSK 0x00ff +#define FPGA_SCRATCH 0x0004 +#define FPGA_BROAD_TYPE 0x0008 +#define FPGA_BROAD_REV_MSK 0x0038 +#define FPGA_BROAD_ID_MSK 0x0007 +#define FPGA_PLL_STATUS 0x0014 +#define BMC_I2C_SCRATCH 0x0020 +#define FPGA_SLAVE_CPLD_REST 0x0030 +#define FPGA_PERIPH_RESET_CTRL 0x0034 +#define FPGA_INT_STATUS 0x0040 +#define FPGA_INT_SRC_STATUS 0x0044 +#define FPGA_INT_FLAG 0x0048 +#define FPGA_INT_MASK 0x004c +#define FPGA_MISC_CTRL 0x0050 +#define FPGA_MISC_STATUS 0x0054 +#define FPGA_AVS_VID_STATUS 0x0068 +#define FPGA_FEATURE_CARD_GPIO 0x0070 +#define FPGA_PORT_XCVR_READY 0x000c + +/* I2C_MASTER BASE ADDR */ +#define I2C_MASTER_FREQ_1 0x0100 +#define I2C_MASTER_CTRL_1 0x0104 +#define I2C_MASTER_STATUS_1 0x0108 +#define I2C_MASTER_DATA_1 0x010c +#define I2C_MASTER_PORT_ID_1 0x0110 +#define I2C_MASTER_CH_1 1 +#define I2C_MASTER_CH_2 2 +#define I2C_MASTER_CH_3 3 +#define I2C_MASTER_CH_4 4 +#define I2C_MASTER_CH_5 5 +#define I2C_MASTER_CH_6 6 +#define I2C_MASTER_CH_7 7 +#define I2C_MASTER_CH_8 8 +#define I2C_MASTER_CH_9 9 +#define I2C_MASTER_CH_10 10 +#define I2C_MASTER_CH_TOTAL I2C_MASTER_CH_10 + +/* SPI_MASTER */ +#define SPI_MASTER_WR_EN 0x1200 /* one bit */ +#define SPI_MASTER_WR_DATA 0x1204 /* 32 bits */ +#define SPI_MASTER_CHK_ID 0x1208 /* one bit */ +#define SPI_MASTER_VERIFY 0x120c /* one bit */ +#define SPI_MASTER_STATUS 0x1210 /* 15 bits */ +#define SPI_MASTER_MODULE_RST 0x1214 /* one bit */ + +/* FPGA FRONT PANEL PORT MGMT */ +#define SFF_PORT_CTRL_BASE 0x4000 +#define SFF_PORT_STATUS_BASE 0x4004 +#define SFF_PORT_INT_STATUS_BASE 0x4008 +#define SFF_PORT_INT_MASK_BASE 0x400c + +#define PORT_XCVR_REGISTER_SIZE 0x1000 + +/* PORT CTRL REGISTER +[31:7] RSVD +[6] LPMOD 6 +[5] RSVD +[4] RST 4 +[3:1] RSVD +[0] TXDIS 0 +*/ +#define CTRL_LPMOD 6 +#define CTRL_RST 4 +#define CTRL_TXDIS 0 + +/* PORT STATUS REGISTER +[31:6] RSVD +[5] IRQ 5 +[4] PRESENT 4 +[3] RSVD +[2] TXFAULT 2 +[1] RXLOS 1 +[0] MODABS 0 +*/ +#define STAT_IRQ 5 +#define STAT_PRESENT 4 +#define STAT_TXFAULT 2 +#define STAT_RXLOS 1 +#define STAT_MODABS 0 + +/* PORT INTRPT REGISTER +[31:6] RSVD +[5] INT_N 5 +[4] PRESENT 4 +[3] RSVD +[2] RSVD +[1] RXLOS 1 +[0] MODABS 0 +*/ +#define INTR_INT_N 5 +#define INTR_PRESENT 4 +#define INTR_RXLOS 1 +#define INTR_MODABS 0 + +/* PORT INT MASK REGISTER +[31:6] RSVD +[5] INT_N 5 +[4] PRESENT 4 +[3] RSVD +[2] RSVD +[1] RXLOS_INT 1 +[0] MODABS 0 +*/ +#define MASK_INT_N 5 +#define MASK_PRESENT 4 +#define MASK_RXLOS 1 +#define MASK_MODABS 0 + +enum { + I2C_SR_BIT_RXAK = 0, + I2C_SR_BIT_MIF, + I2C_SR_BIT_SRW, + I2C_SR_BIT_BCSTM, + I2C_SR_BIT_MAL, + I2C_SR_BIT_MBB, + I2C_SR_BIT_MAAS, + I2C_SR_BIT_MCF +}; + +enum { + I2C_CR_BIT_BCST = 0, + I2C_CR_BIT_RSTA = 2, + I2C_CR_BIT_TXAK, + I2C_CR_BIT_MTX, + I2C_CR_BIT_MSTA, + I2C_CR_BIT_MIEN, + I2C_CR_BIT_MEN, +}; + +/** + * + * The function is i2c algorithm implement to allow master access to + * correct endpoint devices trough the PCA9548 switch devices. + * + * FPGA I2C Master [mutex resource] + * | + * | + * --------------------------- + * | PCA9548(s) | + * ---1--2--3--4--5--6--7--8-- + * | | | | | | | | + * EEPROM ... EEPROM + * + */ + + +#define VIRTUAL_I2C_QSFP_PORT 32 +#define VIRTUAL_I2C_SFP_PORT 1 + +#define SFF_PORT_TOTAL VIRTUAL_I2C_QSFP_PORT + VIRTUAL_I2C_SFP_PORT + +#define VIRTUAL_I2C_BUS_OFFSET 2 +#define CPLD1_SLAVE_ADDR 0x30 +#define CPLD2_SLAVE_ADDR 0x31 + +static struct class* fpgafwclass = NULL; ///< The device-driver class struct pointer +static struct device* fpgafwdev = NULL; ///< The device-driver device struct pointer +static struct platform_device *ds3000_dev; + +#define PCI_VENDOR_ID_TEST 0x1af4 + +#ifndef PCI_VENDOR_ID_XILINX +#define PCI_VENDOR_ID_XILINX 0x10EE +#endif + +#define FPGA_PCIE_DEVICE_ID 0x7021 +#define TEST_PCIE_DEVICE_ID 0x1110 + + +#ifdef DEBUG_KERN +#define info(fmt,args...) printk(KERN_INFO "line %3d : "fmt,__LINE__,##args) +#define check(REG) printk(KERN_INFO "line %3d : %-8s = %2.2X",__LINE__,#REG,ioread8(REG)); +#else +#define info(fmt,args...) +#define check(REG) +#endif + +#define GET_REG_BIT(REG,BIT) ((ioread8(REG) >> BIT) & 0x01) +#define SET_REG_BIT_H(REG,BIT) iowrite8(ioread8(REG) | (0x01 << BIT),REG) +#define SET_REG_BIT_L(REG,BIT) iowrite8(ioread8(REG) & ~(0x01 << BIT),REG) + +static struct mutex fpga_i2c_master_locks[I2C_MASTER_CH_TOTAL]; +/* Store lasted switch address and channel */ +static uint16_t fpga_i2c_lasted_access_port[I2C_MASTER_CH_TOTAL]; + +enum PORT_TYPE { + NONE, + QSFP, + SFP +}; + +struct i2c_switch { + unsigned char master_bus; // I2C bus number + unsigned char switch_addr; // PCA9548 device address, 0xFF if directly connect to a bus. + unsigned char channel; // PCA9548 channel number. If the switch_addr is 0xFF, this value is ignored. + enum PORT_TYPE port_type; // QSFP/SFP tranceiver port type. + char calling_name[20]; // Calling name. +}; + +struct i2c_dev_data { + int portid; + struct i2c_switch pca9548; +}; + +/* PREDEFINED I2C SWITCH DEVICE TOPOLOGY */ +static struct i2c_switch fpga_i2c_bus_dev[] = { + /* BUS2 QSFP Exported as virtual bus */ + {I2C_MASTER_CH_2, 0x72, 0, QSFP, "QSFP1"}, {I2C_MASTER_CH_2, 0x72, 1, QSFP, "QSFP2"}, + {I2C_MASTER_CH_2, 0x72, 2, QSFP, "QSFP3"}, {I2C_MASTER_CH_2, 0x72, 3, QSFP, "QSFP4"}, + {I2C_MASTER_CH_2, 0x72, 4, QSFP, "QSFP5"}, {I2C_MASTER_CH_2, 0x72, 5, QSFP, "QSFP6"}, + {I2C_MASTER_CH_2, 0x72, 6, QSFP, "QSFP7"}, {I2C_MASTER_CH_2, 0x72, 7, QSFP, "QSFP8"}, + {I2C_MASTER_CH_2, 0x73, 0, QSFP, "QSFP9"}, {I2C_MASTER_CH_2, 0x73, 1, QSFP, "QSFP10"}, + {I2C_MASTER_CH_2, 0x73, 2, QSFP, "QSFP11"}, {I2C_MASTER_CH_2, 0x73, 3, QSFP, "QSFP12"}, + {I2C_MASTER_CH_2, 0x73, 4, QSFP, "QSFP13"}, {I2C_MASTER_CH_2, 0x73, 5, QSFP, "QSFP14"}, + {I2C_MASTER_CH_2, 0x73, 6, QSFP, "QSFP15"}, {I2C_MASTER_CH_2, 0x73, 7, QSFP, "QSFP16"}, + {I2C_MASTER_CH_2, 0x74, 0, QSFP, "QSFP17"}, {I2C_MASTER_CH_2, 0x74, 1, QSFP, "QSFP18"}, + {I2C_MASTER_CH_2, 0x74, 2, QSFP, "QSFP19"}, {I2C_MASTER_CH_2, 0x74, 3, QSFP, "QSFP20"}, + {I2C_MASTER_CH_2, 0x74, 4, QSFP, "QSFP21"}, {I2C_MASTER_CH_2, 0x74, 5, QSFP, "QSFP22"}, + {I2C_MASTER_CH_2, 0x74, 6, QSFP, "QSFP23"}, {I2C_MASTER_CH_2, 0x74, 7, QSFP, "QSFP24"}, + {I2C_MASTER_CH_2, 0x75, 0, QSFP, "QSFP25"}, {I2C_MASTER_CH_2, 0x75, 1, QSFP, "QSFP26"}, + {I2C_MASTER_CH_2, 0x75, 2, QSFP, "QSFP27"}, {I2C_MASTER_CH_2, 0x75, 3, QSFP, "QSFP28"}, + {I2C_MASTER_CH_2, 0x75, 4, QSFP, "QSFP29"}, {I2C_MASTER_CH_2, 0x75, 5, QSFP, "QSFP30"}, + {I2C_MASTER_CH_2, 0x75, 6, QSFP, "QSFP31"}, {I2C_MASTER_CH_2, 0x75, 7, QSFP, "QSFP32"}, + /* BUS1 SFP+ Exported as virtual bus */ + {I2C_MASTER_CH_1, 0x72, 0, SFP, "SFP1"}, + /* BUS3 Switchboard CPLD */ + {I2C_MASTER_CH_3, 0xFF, 0, NONE, "I2C_3"}, +}; + +#define VIRTUAL_I2C_PORT_LENGTH ARRAY_SIZE(fpga_i2c_bus_dev) +#define VIRTUAL_I2C_CPLD_INDEX SFF_PORT_TOTAL + +struct fpga_device { + /* data mmio region */ + void __iomem *data_base_addr; + resource_size_t data_mmio_start; + resource_size_t data_mmio_len; +}; + +static struct fpga_device fpga_dev = { + .data_base_addr = 0, + .data_mmio_start = 0, + .data_mmio_len = 0, +}; + +struct ds3000_fpga_data { + struct device *sff_devices[SFF_PORT_TOTAL]; + struct i2c_client *sff_i2c_clients[SFF_PORT_TOTAL]; + struct i2c_adapter *i2c_adapter[VIRTUAL_I2C_PORT_LENGTH]; + struct mutex fpga_lock; // For FPGA internal lock + void __iomem * fpga_read_addr; + uint8_t cpld1_read_addr; + uint8_t cpld2_read_addr; +}; + +struct sff_device_data { + int portid; + enum PORT_TYPE port_type; +}; + +struct ds3000_fpga_data *fpga_data; + +/* + * Kernel object for other module drivers. + * Other module can use these kobject as a parent. + */ + +static struct kobject *fpga = NULL; +static struct kobject *cpld1 = NULL; +static struct kobject *cpld2 = NULL; + +/** + * Device node in sysfs tree. + */ +static struct device *sff_dev = NULL; + +/** + * Show the value of the register set by 'set_fpga_reg_address' + * If the address is not set by 'set_fpga_reg_address' first, + * The version register is selected by default. + * @param buf register value in hextring + * @return number of bytes read, or an error code + */ +static ssize_t get_fpga_reg_value(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // read data from the address + uint32_t data; + data = ioread32(fpga_data->fpga_read_addr); + return sprintf(buf, "0x%8.8x\n", data); +} + +/** + * Store the register address + * @param buf address wanted to be read value of + * @return number of bytes stored, or an error code + */ +static ssize_t set_fpga_reg_address(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + uint32_t addr; + + status = kstrtou32(buf, 0, &addr); + if (status == 0) { + fpga_data->fpga_read_addr = fpga_dev.data_base_addr + addr; + status = count; + } + return status; +} + +/** + * Show value of fpga scratch register + * @param buf register value in hexstring + * @return number of bytes read, or an error code + */ +static ssize_t get_fpga_scratch(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint32_t data; + data = ioread32(fpga_dev.data_base_addr + FPGA_SCRATCH); + data &= 0xffffffff; + return sprintf(buf, "0x%8.8x\n", data); +} + +/** + * Store value of fpga scratch register + * @param buf scratch register value passing from user space + * @return number of bytes stored, or an error code + */ +static ssize_t set_fpga_scratch(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + uint32_t data; + + status = kstrtou32(buf, 0, &data); + if (status == 0) { + iowrite32(data, fpga_dev.data_base_addr + FPGA_SCRATCH); + status = count; + } + return status; +} + +/** + * Store a value in a specific register address + * @param buf the value and address in format '0xhhhh 0xhhhhhhhh' + * @return number of bytes sent by user space, or an error code + */ +static ssize_t set_fpga_reg_value(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + //register is 4 bytes + uint32_t addr; + uint32_t value; + uint32_t mode = 8; + char *tok; + char clone[count]; + char *pclone = clone; + ssize_t status; + + strcpy(clone, buf); + + mutex_lock(&fpga_data->fpga_lock); + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + status = kstrtou32(tok, 0, &addr); + if (status != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + status = kstrtou32(tok, 0, &value); + if (status != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + mode = 32; + } else { + status = kstrtou32(tok, 0, &mode); + if (status != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + } + if (mode == 32) { + iowrite32(value, fpga_dev.data_base_addr + addr); + } else if (mode == 8) { + iowrite8(value, fpga_dev.data_base_addr + addr); + } else { + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + mutex_unlock(&fpga_data->fpga_lock); + return count; +} + +/** + * Show FPGA port XCVR ready status + */ +static ssize_t ready_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u32 data; + unsigned int REGISTER = FPGA_PORT_XCVR_READY; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> 0) & 1U); +} + +/* FPGA attributes */ +static DEVICE_ATTR( getreg, 0600, get_fpga_reg_value, set_fpga_reg_address); +static DEVICE_ATTR( scratch, 0600, get_fpga_scratch, set_fpga_scratch); +static DEVICE_ATTR( setreg, 0200, NULL , set_fpga_reg_value); +static DEVICE_ATTR_RO(ready); + +static struct attribute *fpga_attrs[] = { + &dev_attr_getreg.attr, + &dev_attr_scratch.attr, + &dev_attr_setreg.attr, + &dev_attr_ready.attr, + NULL, +}; + +static struct attribute_group fpga_attr_grp = { + .attrs = fpga_attrs, +}; + +/* SW CPLDs attributes */ +static ssize_t cpld1_getreg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + uint8_t data; + int err; + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, + I2C_SMBUS_READ, fpga_data->cpld1_read_addr, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data); + + if (err < 0) + return err; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t cpld1_getreg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + uint8_t addr; + + status = kstrtou8(buf, 0, &addr); + if (status == 0) { + fpga_data->cpld1_read_addr = addr; + status = count; + } + return status; +} + +static ssize_t cpld1_scratch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + uint8_t data; + int err; + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x01, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data); + if (err < 0) + return err; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t cpld1_scratch_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + // CPLD register is one byte + uint8_t data; + ssize_t status; + int err; + + status = kstrtou8(buf, 0, &data); + if (status != 0) { + return status; + } + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, 0x01, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data); + if (err < 0) + return err; + + return count; +} + +static ssize_t cpld1_setreg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + uint8_t addr, value; + char *tok; + char clone[count]; + char *pclone = clone; + ssize_t status; + int err; + + strcpy(clone, buf); + + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + return -EINVAL; + } + status = kstrtou8(tok, 0, &addr); + if (status != 0) { + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + return -EINVAL; + } + status = kstrtou8(tok, 0, &value); + if (status != 0) { + return -EINVAL; + } + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, addr, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&value); + if (err < 0) + return err; + + return count; +} + +struct device_attribute dev_attr_cpld1_getreg = __ATTR(getreg, 0600, cpld1_getreg_show, cpld1_getreg_store); +struct device_attribute dev_attr_cpld1_scratch = __ATTR(scratch, 0600, cpld1_scratch_show, cpld1_scratch_store); +struct device_attribute dev_attr_cpld1_setreg = __ATTR(setreg, 0200, NULL, cpld1_setreg_store); + +static struct attribute *cpld1_attrs[] = { + &dev_attr_cpld1_getreg.attr, + &dev_attr_cpld1_scratch.attr, + &dev_attr_cpld1_setreg.attr, + NULL, +}; + +static struct attribute_group cpld1_attr_grp = { + .attrs = cpld1_attrs, +}; + +static ssize_t cpld2_getreg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + uint8_t data; + int err; + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, fpga_data->cpld2_read_addr, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data); + if (err < 0) + return err; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t cpld2_getreg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + // CPLD register is one byte + uint8_t addr; + ssize_t status; + + status = kstrtou8(buf, 0, &addr); + if (status == 0) { + fpga_data->cpld2_read_addr = addr; + status = count; + } + return status; +} + +static ssize_t cpld2_scratch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // CPLD register is one byte + uint8_t data; + int err; + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x01, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data); + if (err < 0) + return err; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t cpld2_scratch_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + // CPLD register is one byte + uint8_t data; + int err; + ssize_t status; + + status = kstrtou8(buf, 0, &data); + if (status != 0) { + return -EINVAL; + } + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, 0x01, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data); + if (err < 0) + return err; + + return count; +} + +static ssize_t cpld2_setreg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t addr, value; + char *tok; + char clone[count]; + char *pclone = clone; + ssize_t status; + int err; + + strcpy(clone, buf); + + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + return -EINVAL; + } + status = kstrtou8(tok, 0, &addr); + if (status != 0) { + return -EINVAL; + } + tok = strsep((char**)&pclone, " "); + if (tok == NULL) { + return -EINVAL; + } + status = kstrtou8(tok, 0, &value); + if (status != 0) { + return -EINVAL; + } + + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, addr, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&value); + if (err < 0) + return err; + + return count; +} + +struct device_attribute dev_attr_cpld2_getreg = __ATTR(getreg, 0600, cpld2_getreg_show, cpld2_getreg_store); +struct device_attribute dev_attr_cpld2_scratch = __ATTR(scratch, 0600, cpld2_scratch_show, cpld2_scratch_store); +struct device_attribute dev_attr_cpld2_setreg = __ATTR(setreg, 0200, NULL, cpld2_setreg_store); + +static struct attribute *cpld2_attrs[] = { + &dev_attr_cpld2_getreg.attr, + &dev_attr_cpld2_scratch.attr, + &dev_attr_cpld2_setreg.attr, + NULL, +}; + +static struct attribute_group cpld2_attr_grp = { + .attrs = cpld2_attrs, +}; + +/* QSFP/SFP+ attributes */ +static ssize_t qsfp_modirq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> STAT_IRQ) & 1U); +} +DEVICE_ATTR_RO(qsfp_modirq); + +static ssize_t qsfp_modprs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> STAT_PRESENT) & 1U); +} +DEVICE_ATTR_RO(qsfp_modprs); + +static ssize_t sfp_txfault_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> STAT_TXFAULT) & 1U); +} +DEVICE_ATTR_RO(sfp_txfault); + +static ssize_t sfp_rxlos_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> STAT_RXLOS) & 1U); +} +DEVICE_ATTR_RO(sfp_rxlos); + +static ssize_t sfp_modabs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> STAT_MODABS) & 1U); +} +DEVICE_ATTR_RO(sfp_modabs); + +static ssize_t qsfp_lpmode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> CTRL_LPMOD) & 1U); +} +static ssize_t qsfp_lpmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + uint32_t value; + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtou32(buf, 0, &value); + if (status == 0) { + // check if value is 0 clear + data = ioread32(fpga_dev.data_base_addr + REGISTER); + if (!value) + data = data & ~( (u32)0x1 << CTRL_LPMOD); + else + data = data | ((u32)0x1 << CTRL_LPMOD); + iowrite32(data, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(qsfp_lpmode); + +static ssize_t qsfp_reset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> CTRL_RST) & 1U); +} + +static ssize_t qsfp_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + uint32_t value; + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtou32(buf, 0, &value); + if (status == 0) { + // check if value is 0 clear + data = ioread32(fpga_dev.data_base_addr + REGISTER); + if (!value) + data = data & ~( (u32)0x1 << CTRL_RST); + else + data = data | ((u32)0x1 << CTRL_RST); + iowrite32(data, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(qsfp_reset); + +static ssize_t qsfp_isr_flags_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT); + + mutex_lock(&fpga_data->fpga_lock); + data = (u8) ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + + /* + * Unify the return pattern to 2-bit + * [1] : module interrupt + * [0] : presence + */ + data = data & valid_bits; + data = data >> 4; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t qsfp_isr_flags_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + u32 value; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT); + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtou32(buf, 0, &value); + if (status == 0) { + value = value << 4; + value = value & valid_bits; + iowrite32(value, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(qsfp_isr_flags); + +static ssize_t qsfp_isr_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT); + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + + /* + * Unify the return pattern to 2-bit + * [1] : module interrupt + * [0] : presence + */ + data = data & valid_bits; + data = data >> 4; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t qsfp_isr_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + u32 value; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT); + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtou32(buf, 0, &value); + if (status == 0) { + value = value << 4; + value = value & valid_bits; + iowrite32(value, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(qsfp_isr_mask); + +static ssize_t sfp_txdisable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + return sprintf(buf, "%d\n", (data >> CTRL_TXDIS) & 1U); +} +static ssize_t sfp_txdisable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + long value; + u32 data; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10; + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtol(buf, 0, &value); + if (status == 0) { + // check if value is 0 clear + data = ioread32(fpga_dev.data_base_addr + REGISTER); + if (!value) + data = data & ~( (u32)0x1 << CTRL_TXDIS); + else + data = data | ((u32)0x1 << CTRL_TXDIS); + iowrite32(data, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(sfp_txdisable); + +static ssize_t sfp_isr_flags_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_RXLOS) | BIT(INTR_MODABS); + + mutex_lock(&fpga_data->fpga_lock); + data = (u8) ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + + data = data & valid_bits; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t sfp_isr_flags_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + u32 value; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT); + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtou32(buf, 0, &value); + if (status == 0) { + value = value & valid_bits; + iowrite32(value, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(sfp_isr_flags); + +static ssize_t sfp_isr_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 data; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_RXLOS) | BIT(INTR_MODABS); + + mutex_lock(&fpga_data->fpga_lock); + data = ioread32(fpga_dev.data_base_addr + REGISTER); + mutex_unlock(&fpga_data->fpga_lock); + + data = data & valid_bits; + + return sprintf(buf, "0x%2.2x\n", data); +} + +static ssize_t sfp_isr_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t status; + u32 value; + u8 valid_bits; + struct sff_device_data *dev_data = dev_get_drvdata(dev); + unsigned int portid = dev_data->portid; + unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10; + valid_bits = BIT(INTR_RXLOS) | BIT(INTR_MODABS); + + mutex_lock(&fpga_data->fpga_lock); + status = kstrtou32(buf, 0, &value); + if (status == 0) { + value = value & valid_bits; + iowrite32(value, fpga_dev.data_base_addr + REGISTER); + status = count; + } + mutex_unlock(&fpga_data->fpga_lock); + return status; +} +DEVICE_ATTR_RW(sfp_isr_mask); + +static struct attribute *sff_attrs[] = { + &dev_attr_qsfp_modirq.attr, + &dev_attr_qsfp_modprs.attr, + &dev_attr_qsfp_lpmode.attr, + &dev_attr_qsfp_reset.attr, + &dev_attr_qsfp_isr_flags.attr, + &dev_attr_qsfp_isr_mask.attr, + &dev_attr_sfp_txfault.attr, + &dev_attr_sfp_rxlos.attr, + &dev_attr_sfp_modabs.attr, + &dev_attr_sfp_txdisable.attr, + &dev_attr_sfp_isr_flags.attr, + &dev_attr_sfp_isr_mask.attr, + NULL, +}; + +static struct attribute_group sff_attr_grp = { + .attrs = sff_attrs, +}; + +static const struct attribute_group *sff_attr_grps[] = { + &sff_attr_grp, + NULL +}; + + +static ssize_t port_led_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // value can be "nomal", "test" + __u8 led_mode_1, led_mode_2; + int err; + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x09, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_mode_1); + if (err < 0) + return err; + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x09, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_mode_2); + if (err < 0) + return err; + + return sprintf(buf, "%s %s\n", + led_mode_1 ? "test" : "normal", + led_mode_2 ? "test" : "normal"); +} +static ssize_t port_led_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int status; + __u8 led_mode_1; + if (sysfs_streq(buf, "test")) { + led_mode_1 = 0x01; + } else if (sysfs_streq(buf, "normal")) { + led_mode_1 = 0x00; + } else { + return -EINVAL; + } + status = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, 0x09, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_mode_1); + status = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, 0x09, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_mode_1); + return count; +} +DEVICE_ATTR_RW(port_led_mode); + +// Only work when port_led_mode set to 1 +static ssize_t port_led_color_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + // value can be "off", "green", "amber", "both" + __u8 led_color1, led_color2; + int err; + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x0A, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_color1); + if (err < 0) + return err; + err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x0A, + I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_color2); + if (err < 0) + return err; + return sprintf(buf, "%s %s\n", + led_color1 == 0x03 ? "off" : led_color1 == 0x02 ? "green" : led_color1 == 0x01 ? "amber" : "both", + led_color2 == 0x03 ? "off" : led_color2 == 0x02 ? "green" : led_color2 == 0x01 ? "amber" : "both"); +} + +static ssize_t port_led_color_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int status; + __u8 led_color; + if (sysfs_streq(buf, "off")) { + led_color = 0x03; + } else if (sysfs_streq(buf, "green")) { + led_color = 0x02; + } else if (sysfs_streq(buf, "amber")) { + led_color = 0x01; + } else if (sysfs_streq(buf, "both")) { + led_color = 0x00; + } else { + status = -EINVAL; + return status; + } + status = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD1_SLAVE_ADDR, 0x00, + I2C_SMBUS_WRITE, 0x0A, I2C_SMBUS_BYTE_DATA, + (union i2c_smbus_data*)&led_color); + status = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], + CPLD2_SLAVE_ADDR, 0x00, + I2C_SMBUS_WRITE, 0x0A, I2C_SMBUS_BYTE_DATA, + (union i2c_smbus_data*)&led_color); + return count; +} +DEVICE_ATTR_RW(port_led_color); + +static struct attribute *sff_led_test[] = { + &dev_attr_port_led_mode.attr, + &dev_attr_port_led_color.attr, + NULL, +}; + +static struct attribute_group sff_led_test_grp = { + .attrs = sff_led_test, +}; + +static struct device * ds3000_sff_init(int portid) { + struct sff_device_data *new_data; + struct device *new_device; + + new_data = kzalloc(sizeof(*new_data), GFP_KERNEL); + if (!new_data) { + printk(KERN_ALERT "Cannot alloc sff device data @port%d", portid); + return NULL; + } + /* The QSFP port ID start from 1 */ + new_data->portid = portid + 1; + new_data->port_type = fpga_i2c_bus_dev[portid].port_type; + new_device = device_create_with_groups(fpgafwclass, sff_dev, + MKDEV(0, 0), new_data, sff_attr_grps, "%s", + fpga_i2c_bus_dev[portid].calling_name); + if (IS_ERR(new_device)) { + printk(KERN_ALERT "Cannot create sff device @port%d", portid); + kfree(new_data); + return NULL; + } + return new_device; +} + +static int i2c_wait_ack(struct i2c_adapter *a, unsigned long timeout, int writing) { + int error = 0; + int Status; + + struct i2c_dev_data *new_data = i2c_get_adapdata(a); + void __iomem *pci_bar = fpga_dev.data_base_addr; + + unsigned int REG_FDR0; + unsigned int REG_CR0; + unsigned int REG_SR0; + unsigned int REG_DR0; + unsigned int REG_ID0; + + unsigned int master_bus = new_data->pca9548.master_bus; + + if (master_bus < I2C_MASTER_CH_1 || master_bus > I2C_MASTER_CH_TOTAL) { + error = -EINVAL; + return error; + } + + REG_FDR0 = I2C_MASTER_FREQ_1 + (master_bus - 1) * 0x0100; + REG_CR0 = I2C_MASTER_CTRL_1 + (master_bus - 1) * 0x0100; + REG_SR0 = I2C_MASTER_STATUS_1 + (master_bus - 1) * 0x0100; + REG_DR0 = I2C_MASTER_DATA_1 + (master_bus - 1) * 0x0100; + REG_ID0 = I2C_MASTER_PORT_ID_1 + (master_bus - 1) * 0x0100; + + check(pci_bar + REG_SR0); + check(pci_bar + REG_CR0); + + timeout = jiffies + msecs_to_jiffies(timeout); + while (1) { + Status = ioread8(pci_bar + REG_SR0); + if (jiffies > timeout) { + info("Status %2.2X", Status); + info("Error Timeout"); + error = -ETIMEDOUT; + break; + } + + + if (Status & (1 << I2C_SR_BIT_MIF)) { + break; + } + + if (writing == 0 && (Status & (1 << I2C_SR_BIT_MCF))) { + break; + } + } + Status = ioread8(pci_bar + REG_SR0); + iowrite8(0, pci_bar + REG_SR0); + + if (error < 0) { + info("Status %2.2X", Status); + return error; + } + + if (!(Status & (1 << I2C_SR_BIT_MCF))) { + info("Error Unfinish"); + return -EIO; + } + + if (Status & (1 << I2C_SR_BIT_MAL)) { + info("Error MAL"); + return -EAGAIN; + } + + if (Status & (1 << I2C_SR_BIT_RXAK)) { + info( "SL No Acknowlege"); + if (writing) { + info("Error No Acknowlege"); + iowrite8(1 << I2C_CR_BIT_MEN, pci_bar + REG_CR0); + return -ENXIO; + } + } else { + info( "SL Acknowlege"); + } + + return 0; +} + +static int smbus_access(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data) +{ + int error = 0; + int cnt = 0; + int bid = 0; + struct i2c_dev_data *dev_data; + void __iomem *pci_bar; + unsigned int portid, master_bus; + + unsigned int REG_FDR0; + unsigned int REG_CR0; + unsigned int REG_SR0; + unsigned int REG_DR0; + unsigned int REG_ID0; + + REG_FDR0 = 0; + REG_CR0 = 0; + REG_SR0 = 0; + REG_DR0 = 0; + REG_ID0 = 0; + + /* Write the command register */ + dev_data = i2c_get_adapdata(adapter); + portid = dev_data->portid; + pci_bar = fpga_dev.data_base_addr; + +#ifdef DEBUG_KERN + printk(KERN_INFO "portid %2d|@ 0x%2.2X|f 0x%4.4X|(%d)%-5s| (%d)%-15s|CMD %2.2X " + , portid, addr, flags, rw, rw == 1 ? "READ " : "WRITE" + , size, size == 0 ? "QUICK" : + size == 1 ? "BYTE" : + size == 2 ? "BYTE_DATA" : + size == 3 ? "WORD_DATA" : + size == 4 ? "PROC_CALL" : + size == 5 ? "BLOCK_DATA" : + size == 8 ? "I2C_BLOCK_DATA" : "ERROR" + , cmd); +#endif + /* Map the size to what the chip understands */ + switch (size) { + case I2C_SMBUS_QUICK: + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: + break; + default: + printk(KERN_INFO "Unsupported transaction %d\n", size); + error = -EOPNOTSUPP; + goto Done; + } + + master_bus = dev_data->pca9548.master_bus; + + if (master_bus < I2C_MASTER_CH_1 || master_bus > I2C_MASTER_CH_TOTAL) { + error = -ENXIO; + goto Done; + } + + REG_FDR0 = I2C_MASTER_FREQ_1 + (master_bus - 1) * 0x0100; + REG_CR0 = I2C_MASTER_CTRL_1 + (master_bus - 1) * 0x0100; + REG_SR0 = I2C_MASTER_STATUS_1 + (master_bus - 1) * 0x0100; + REG_DR0 = I2C_MASTER_DATA_1 + (master_bus - 1) * 0x0100; + REG_ID0 = I2C_MASTER_PORT_ID_1 + (master_bus - 1) * 0x0100; + + iowrite8(portid, pci_bar + REG_ID0); + + ////[S][ADDR/R] + // Clear status register + iowrite8(0, pci_bar + REG_SR0); + iowrite8(1 << I2C_CR_BIT_MIEN | + 1 << I2C_CR_BIT_MTX | + 1 << I2C_CR_BIT_MSTA , pci_bar + REG_CR0); + SET_REG_BIT_H(pci_bar + REG_CR0, I2C_CR_BIT_MEN); + + if (rw == I2C_SMBUS_READ && + (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE)) { + // sent device address with Read mode + iowrite8(addr << 1 | 0x01, pci_bar + REG_DR0); + } else { + // sent device address with Write mode + iowrite8(addr << 1 | 0x00, pci_bar + REG_DR0); + } + + + + info( "MS Start"); + + //// Wait {A} + error = i2c_wait_ack(adapter, 12, 1); + if (error < 0) { + info( "get error %d", error); + goto Done; + } + + //// [CMD]{A} + if (size == I2C_SMBUS_BYTE_DATA || + size == I2C_SMBUS_WORD_DATA || + size == I2C_SMBUS_BLOCK_DATA || + size == I2C_SMBUS_I2C_BLOCK_DATA || + (size == I2C_SMBUS_BYTE && rw == I2C_SMBUS_WRITE)) { + + // sent command code to data register + iowrite8(cmd, pci_bar + REG_DR0); + info( "MS Send CMD 0x%2.2X", cmd); + + // Wait {A} + error = i2c_wait_ack(adapter, 12, 1); + if (error < 0) { + info( "get error %d", error); + goto Done; + } + } + + switch (size) { + case I2C_SMBUS_BYTE_DATA: + cnt = 1; break; + case I2C_SMBUS_WORD_DATA: + cnt = 2; break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: + /* In block data modes keep number of byte in block[0] */ + cnt = data->block[0]; + break; + default: + cnt = 0; break; + } + + // [CNT] used only block data write + if (size == I2C_SMBUS_BLOCK_DATA && rw == I2C_SMBUS_WRITE) { + + iowrite8(cnt, pci_bar + REG_DR0); + info( "MS Send CNT 0x%2.2X", cnt); + + // Wait {A} + error = i2c_wait_ack(adapter, 12, 1); + if (error < 0) { + info( "get error %d", error); + goto Done; + } + } + + // [DATA]{A} + if ( rw == I2C_SMBUS_WRITE && ( + size == I2C_SMBUS_BYTE || + size == I2C_SMBUS_BYTE_DATA || + size == I2C_SMBUS_WORD_DATA || + size == I2C_SMBUS_BLOCK_DATA || + size == I2C_SMBUS_I2C_BLOCK_DATA + )) { + bid = 0; + info( "MS prepare to sent [%d bytes]", cnt); + if (size == I2C_SMBUS_BLOCK_DATA || size == I2C_SMBUS_I2C_BLOCK_DATA) { + bid = 1; // block[0] is cnt; + cnt += 1; // offset from block[0] + } + for (; bid < cnt; bid++) { + + iowrite8(data->block[bid], pci_bar + REG_DR0); + info( " Data > %2.2X", data->block[bid]); + // Wait {A} + error = i2c_wait_ack(adapter, 12, 1); + if (error < 0) { + goto Done; + } + } + + } + + // REPEATE START + if ( rw == I2C_SMBUS_READ && ( + size == I2C_SMBUS_BYTE_DATA || + size == I2C_SMBUS_WORD_DATA || + size == I2C_SMBUS_BLOCK_DATA || + size == I2C_SMBUS_I2C_BLOCK_DATA + )) { + info( "MS Repeated Start"); + + SET_REG_BIT_L(pci_bar + REG_CR0, I2C_CR_BIT_MEN); + iowrite8(1 << I2C_CR_BIT_MIEN | + 1 << I2C_CR_BIT_MTX | + 1 << I2C_CR_BIT_MSTA | + 1 << I2C_CR_BIT_RSTA , pci_bar + REG_CR0); + SET_REG_BIT_H(pci_bar + REG_CR0, I2C_CR_BIT_MEN); + + // sent Address with Read mode + iowrite8( addr << 1 | 0x1 , pci_bar + REG_DR0); + + // Wait {A} + error = i2c_wait_ack(adapter, 12, 1); + if (error < 0) { + goto Done; + } + + } + + if ( rw == I2C_SMBUS_READ && ( + size == I2C_SMBUS_BYTE || + size == I2C_SMBUS_BYTE_DATA || + size == I2C_SMBUS_WORD_DATA || + size == I2C_SMBUS_BLOCK_DATA || + size == I2C_SMBUS_I2C_BLOCK_DATA + )) { + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + cnt = 1; break; + case I2C_SMBUS_WORD_DATA: + cnt = 2; break; + case I2C_SMBUS_BLOCK_DATA: + // will be changed after recived first data + cnt = 3; break; + case I2C_SMBUS_I2C_BLOCK_DATA: + cnt = data->block[0]; break; + default: + cnt = 0; break; + } + + bid = 0; + info( "MS Receive"); + + //set to Receive mode + iowrite8(1 << I2C_CR_BIT_MEN | + 1 << I2C_CR_BIT_MIEN | + 1 << I2C_CR_BIT_MSTA , pci_bar + REG_CR0); + + for (bid = -1; bid < cnt; bid++) { + + // Wait for byte transfer + error = i2c_wait_ack(adapter, 12, 0); + if (error < 0) { + goto Done; + } + + if (bid == cnt - 2) { + info( "SET NAK"); + SET_REG_BIT_H(pci_bar + REG_CR0, I2C_CR_BIT_TXAK); + } + + if (bid < 0) { + ioread8(pci_bar + REG_DR0); + info( "READ Dummy Byte" ); + } else { + + if (bid == cnt - 1) { + info ( "SET STOP in read loop"); + SET_REG_BIT_L(pci_bar + REG_CR0, I2C_CR_BIT_MSTA); + } + if (size == I2C_SMBUS_I2C_BLOCK_DATA) { + // block[0] is read length + data->block[bid + 1] = ioread8(pci_bar + REG_DR0); + } else { + data->block[bid] = ioread8(pci_bar + REG_DR0); + } + info( "DATA IN [%d] %2.2X", bid, data->block[bid]); + + if (size == I2C_SMBUS_BLOCK_DATA && bid == 0) { + cnt = data->block[0] + 1; + } + } + } + } + + // [P] + SET_REG_BIT_L(pci_bar + REG_CR0, I2C_CR_BIT_MSTA); + info( "MS STOP"); + +Done: + iowrite8(1 << I2C_CR_BIT_MEN, pci_bar + REG_CR0); + check(pci_bar + REG_CR0); + check(pci_bar + REG_SR0); +#ifdef DEBUG_KERN + printk(KERN_INFO "END --- Error code %d", error); +#endif + + return error; +} + +/** + * Wrapper of smbus_access access with PCA9548 I2C switch management. + * This function set PCA9548 switches to the proper slave channel. + * Only one channel among switches chip is selected during communication time. + * + * Note: If the bus does not have any PCA9548 on it, the switch_addr must be + * set to 0xFF, it will use normal smbus_access function. + */ +static int fpga_i2c_access(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data) +{ + int error = 0; + struct i2c_dev_data *dev_data; + unsigned char master_bus; + unsigned char switch_addr; + unsigned char channel; + uint16_t prev_port = 0; + unsigned char prev_switch; + unsigned char prev_ch; + int retry; + + dev_data = i2c_get_adapdata(adapter); + master_bus = dev_data->pca9548.master_bus; + switch_addr = dev_data->pca9548.switch_addr; + channel = dev_data->pca9548.channel; + + // Acquire the master resource. + mutex_lock(&fpga_i2c_master_locks[master_bus - 1]); + prev_port = fpga_i2c_lasted_access_port[master_bus - 1]; + prev_switch = (unsigned char)(prev_port >> 8) & 0xFF; + prev_ch = (unsigned char)(prev_port & 0xFF); + + if (switch_addr != 0xFF) { + + // Check lasted access switch address on a master + if ( prev_switch != switch_addr && prev_switch != 0 ) { + // reset prev_port PCA9548 chip + retry = 3; + while (retry--) { + error = smbus_access(adapter, (u16)(prev_switch), flags, + I2C_SMBUS_WRITE, 0x00, + I2C_SMBUS_BYTE, NULL); + if (error >= 0) { + break; + } else { + dev_dbg(&adapter->dev, + "Failed to deselect ch %d of 0x%x, CODE %d\n", + prev_ch, prev_switch, error); + } + } + if (retry < 0) { + goto release_unlock; + } + // set PCA9548 to current channel + retry = 3; + while (retry--) { + error = smbus_access(adapter, switch_addr, flags, + I2C_SMBUS_WRITE, 1 << channel, + I2C_SMBUS_BYTE, NULL); + if (error >= 0) { + break; + } else { + dev_dbg(&adapter->dev, + "Failed to deselect ch %d of 0x%x, CODE %d\n", + prev_ch, prev_switch, error); + } + } + if (retry < 0) { + goto release_unlock; + } + // update lasted port + fpga_i2c_lasted_access_port[master_bus - 1] = switch_addr << 8 | channel; + + } else { + // check if channel is also changes + if ( prev_ch != channel || prev_switch == 0 ) { + // set new PCA9548 at switch_addr to current + retry = 3; + while (retry--) { + error = smbus_access(adapter, switch_addr, flags, + I2C_SMBUS_WRITE, 1 << channel, + I2C_SMBUS_BYTE, NULL); + if (error >= 0) { + break; + } else { + dev_dbg(&adapter->dev, + "Failed to deselect ch %d of 0x%x, CODE %d\n", + prev_ch, prev_switch, error); + } + } + if (retry < 0) { + goto release_unlock; + } + // update lasted port + fpga_i2c_lasted_access_port[master_bus - 1] = switch_addr << 8 | channel; + } + } + } + + // Do SMBus communication + error = smbus_access(adapter, addr, flags, rw, cmd, size, data); + if (error < 0) { + dev_dbg( &adapter->dev, + "smbus_xfer failed (%d) @ 0x%2.2X|f 0x%4.4X|(%d)%-5s| (%d)%-10s|CMD %2.2X " + , error, addr, flags, rw, rw == 1 ? "READ " : "WRITE" + , size, size == 0 ? "QUICK" : + size == 1 ? "BYTE" : + size == 2 ? "BYTE_DATA" : + size == 3 ? "WORD_DATA" : + size == 4 ? "PROC_CALL" : + size == 5 ? "BLOCK_DATA" : + size == 8 ? "I2C_BLOCK_DATA" : "ERROR" + , cmd); + } + +release_unlock: + mutex_unlock(&fpga_i2c_master_locks[master_bus - 1]); + dev_dbg(&adapter->dev, "switch ch %d of 0x%x -> ch %d of 0x%x\n", + prev_ch, prev_switch, channel, switch_addr); + return error; +} + + + +/** + * A callback function show available smbus functions. + */ +static u32 fpga_i2c_func(struct i2c_adapter *a) +{ + return I2C_FUNC_SMBUS_QUICK | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm ds3000_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 * ds3000_i2c_init(struct platform_device *pdev, + int portid, int bus_number_offset) +{ + int error; + struct i2c_adapter *new_adapter; + struct i2c_dev_data *new_data; + void __iomem *i2c_freq_base_reg; + + new_adapter = kzalloc(sizeof(*new_adapter), GFP_KERNEL); + if (!new_adapter) { + printk(KERN_ALERT "Cannot alloc i2c adapter for %s", + fpga_i2c_bus_dev[portid].calling_name); + return NULL; + } + + new_adapter->owner = THIS_MODULE; + new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + new_adapter->algo = &ds3000_i2c_algorithm; + /* If the bus offset is -1, use dynamic bus number */ + if (bus_number_offset == -1) { + new_adapter->nr = -1; + } else { + new_adapter->nr = bus_number_offset + portid; + } + + new_data = kzalloc(sizeof(*new_data), GFP_KERNEL); + if (!new_data) { + printk(KERN_ALERT "Cannot alloc i2c data for %s", + fpga_i2c_bus_dev[portid].calling_name); + kfree_sensitive(new_adapter); + return NULL; + } + + new_data->portid = portid; + new_data->pca9548.master_bus = fpga_i2c_bus_dev[portid].master_bus; + new_data->pca9548.switch_addr = fpga_i2c_bus_dev[portid].switch_addr; + new_data->pca9548.channel = fpga_i2c_bus_dev[portid].channel; + strcpy(new_data->pca9548.calling_name, fpga_i2c_bus_dev[portid].calling_name); + + snprintf(new_adapter->name, sizeof(new_adapter->name), + "SMBus I2C Adapter PortID: %s", new_data->pca9548.calling_name); + + i2c_freq_base_reg = fpga_dev.data_base_addr + I2C_MASTER_FREQ_1; + iowrite8(0x07, i2c_freq_base_reg + (new_data->pca9548.master_bus - 1) * 0x100); // 0x07 400kHz + i2c_set_adapdata(new_adapter, new_data); + error = i2c_add_numbered_adapter(new_adapter); + if (error < 0) { + printk(KERN_ALERT "Cannot add i2c adapter %s", new_data->pca9548.calling_name); + kfree_sensitive(new_adapter); + kfree_sensitive(new_data); + return NULL; + } + + return new_adapter; +}; + +/** + * Board info for QSFP/SFP+ eeprom. + * Note: Using sff8436 as I2C eeprom driver. + */ +static struct i2c_board_info sff8436_eeprom_info[] = { + { I2C_BOARD_INFO("optoe1", 0x50) }, + { I2C_BOARD_INFO("optoe2", 0x50) }, +}; + +static int ds3000_drv_probe(struct platform_device *pdev) +{ + int ret = 0; + int portid_count; + uint8_t cpld1_version, cpld2_version; + uint16_t prev_i2c_switch = 0; + struct sff_device_data *sff_data; + + /* The device class need to be instantiated before this function called */ + BUG_ON(fpgafwclass == NULL); + + fpga_data = devm_kzalloc(&pdev->dev, sizeof(struct ds3000_fpga_data), + GFP_KERNEL); + + if (!fpga_data) + return -ENOMEM; + + // Set default read address to VERSION + fpga_data->fpga_read_addr = fpga_dev.data_base_addr + FPGA_VERSION; + fpga_data->cpld1_read_addr = 0x00; + fpga_data->cpld2_read_addr = 0x00; + + mutex_init(&fpga_data->fpga_lock); + + for (ret = I2C_MASTER_CH_1 ; ret <= I2C_MASTER_CH_TOTAL; ret++) { + mutex_init(&fpga_i2c_master_locks[ret - 1]); + } + + fpga = kobject_create_and_add("FPGA", &pdev->dev.kobj); + if (!fpga) { + kfree_sensitive(fpga_data); + return -ENOMEM; + } + + ret = sysfs_create_group(fpga, &fpga_attr_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create FPGA sysfs attributes\n"); + kobject_put(fpga); + kfree_sensitive(fpga_data); + return ret; + } + + cpld1 = kobject_create_and_add("CPLD1", &pdev->dev.kobj); + if (!cpld1) { + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kfree_sensitive(fpga_data); + return -ENOMEM; + } + ret = sysfs_create_group(cpld1, &cpld1_attr_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create CPLD1 sysfs attributes\n"); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kfree_sensitive(fpga_data); + return ret; + } + + cpld2 = kobject_create_and_add("CPLD2", &pdev->dev.kobj); + if (!cpld2) { + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kfree_sensitive(fpga_data); + return -ENOMEM; + } + ret = sysfs_create_group(cpld2, &cpld2_attr_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create CPLD2 sysfs attributes\n"); + kobject_put(cpld2); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kfree_sensitive(fpga_data); + return ret; + } + + sff_dev = device_create(fpgafwclass, NULL, MKDEV(0, 0), NULL, "sff_device"); + if (IS_ERR(sff_dev)) { + printk(KERN_ERR "Failed to create sff device\n"); + sysfs_remove_group(cpld2, &cpld2_attr_grp); + kobject_put(cpld2); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kfree_sensitive(fpga_data); + return PTR_ERR(sff_dev); + } + + ret = sysfs_create_group(&sff_dev->kobj, &sff_led_test_grp); + if (ret != 0) { + printk(KERN_ERR "Cannot create SFF attributes\n"); + device_destroy(fpgafwclass, MKDEV(0, 0)); + sysfs_remove_group(cpld2, &cpld2_attr_grp); + kobject_put(cpld2); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kfree_sensitive(fpga_data); + return ret; + } + + ret = sysfs_create_link(&pdev->dev.kobj, &sff_dev->kobj, "SFF"); + if (ret != 0) { + sysfs_remove_group(&sff_dev->kobj, &sff_led_test_grp); + device_destroy(fpgafwclass, MKDEV(0, 0)); + sysfs_remove_group(cpld2, &cpld2_attr_grp); + kobject_put(cpld2); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + kobject_put(cpld1); + sysfs_remove_group(fpga, &fpga_attr_grp); + kobject_put(fpga); + kfree_sensitive(fpga_data); + return ret; + } + + for (portid_count = 0 ; portid_count < VIRTUAL_I2C_PORT_LENGTH ; portid_count++) { + fpga_data->i2c_adapter[portid_count] = ds3000_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] = ds3000_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_client_device(i2c_adap, &sff8436_eeprom_info[0]); + } else { + fpga_data->sff_i2c_clients[portid_count] = i2c_new_client_device(i2c_adap, &sff8436_eeprom_info[1]); + } + sff_data = NULL; + sysfs_create_link(&fpga_data->sff_devices[portid_count]->kobj, + &fpga_data->sff_i2c_clients[portid_count]->dev.kobj, + "i2c"); + } + + } + + printk(KERN_INFO "Virtual I2C buses created\n"); + +#ifdef TEST_MODE + return 0; +#endif + fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], CPLD1_SLAVE_ADDR, 0x00, + I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&cpld1_version); + fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], CPLD2_SLAVE_ADDR, 0x00, + I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&cpld2_version); + + printk(KERN_INFO "CPLD1 VERSON: %2.2x\n", cpld1_version); + printk(KERN_INFO "CPLD2 VERSON: %2.2x\n", cpld2_version); + + /* Init I2C buses that has PCA9548 switch device. */ + for (portid_count = 0; portid_count < VIRTUAL_I2C_PORT_LENGTH; portid_count++) { + + struct i2c_dev_data *dev_data; + 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 ds3000_drv_remove(struct platform_device *pdev) +{ + int portid_count; + struct sff_device_data *rem_data; + + for (portid_count = 0; portid_count < SFF_PORT_TOTAL; portid_count++) { + sysfs_remove_link(&fpga_data->sff_devices[portid_count]->kobj, "i2c"); + i2c_unregister_device(fpga_data->sff_i2c_clients[portid_count]); + } + + for (portid_count = 0 ; portid_count < VIRTUAL_I2C_PORT_LENGTH ; portid_count++) { + if (fpga_data->i2c_adapter[portid_count] != NULL) { + info(KERN_INFO "<%x>", fpga_data->i2c_adapter[portid_count]); + i2c_del_adapter(fpga_data->i2c_adapter[portid_count]); + } + } + + for (portid_count = 0; portid_count < SFF_PORT_TOTAL; portid_count++) { + if (fpga_data->sff_devices[portid_count] != NULL) { + rem_data = dev_get_drvdata(fpga_data->sff_devices[portid_count]); + device_unregister(fpga_data->sff_devices[portid_count]); + put_device(fpga_data->sff_devices[portid_count]); + kfree(rem_data); + } + } + + sysfs_remove_group(fpga, &fpga_attr_grp); + sysfs_remove_group(cpld1, &cpld1_attr_grp); + sysfs_remove_group(cpld2, &cpld2_attr_grp); + sysfs_remove_group(&sff_dev->kobj, &sff_led_test_grp); + kobject_put(fpga); + kobject_put(cpld1); + kobject_put(cpld2); + device_destroy(fpgafwclass, MKDEV(0, 0)); + devm_kfree(&pdev->dev, fpga_data); + return 0; +} + +static struct platform_driver ds3000_drv = { + .probe = ds3000_drv_probe, + .remove = __exit_p(ds3000_drv_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +#ifdef TEST_MODE +#define FPGA_PCI_BAR_NUM 2 +#else +#define FPGA_PCI_BAR_NUM 0 +#endif + +static const struct pci_device_id fpga_id_table[] = { + { PCI_VDEVICE(XILINX, FPGA_PCIE_DEVICE_ID) }, + { PCI_VDEVICE(TEST, TEST_PCIE_DEVICE_ID) }, + {0, } +}; + +MODULE_DEVICE_TABLE(pci, fpga_id_table); + +static int fpga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int err; + struct device *dev = &pdev->dev; + uint32_t fpga_version; + + if ((err = pci_enable_device(pdev))) { + dev_err(dev, "pci_enable_device probe error %d for device %s\n", + err, pci_name(pdev)); + return err; + } + + if ((err = pci_request_regions(pdev, FPGA_PCI_NAME)) < 0) { + dev_err(dev, "pci_request_regions error %d\n", err); + goto pci_disable; + } + + /* bar0: data mmio region */ + fpga_dev.data_mmio_start = pci_resource_start(pdev, FPGA_PCI_BAR_NUM); + fpga_dev.data_mmio_len = pci_resource_len(pdev, FPGA_PCI_BAR_NUM); + fpga_dev.data_base_addr = pci_iomap(pdev, FPGA_PCI_BAR_NUM, 0); + if (!fpga_dev.data_base_addr) { + dev_err(dev, "cannot iomap region of size %lu\n", + (unsigned long)fpga_dev.data_mmio_len); + goto pci_release; + } + dev_info(dev, "data_mmio iomap base = 0x%lx \n", + (unsigned long)fpga_dev.data_base_addr); + dev_info(dev, "data_mmio_start = 0x%lx data_mmio_len = %lu\n", + (unsigned long)fpga_dev.data_mmio_start, + (unsigned long)fpga_dev.data_mmio_len); + + printk(KERN_INFO "FPGA PCIe driver probe OK.\n"); + printk(KERN_INFO "FPGA ioremap registers of size %lu\n", (unsigned long)fpga_dev.data_mmio_len); + printk(KERN_INFO "FPGA Virtual BAR %d at %8.8lx - %8.8lx\n", FPGA_PCI_BAR_NUM, + (unsigned long)fpga_dev.data_base_addr, + (unsigned long)(fpga_dev.data_base_addr + fpga_dev.data_mmio_len)); + printk(KERN_INFO ""); + fpga_version = ioread32(fpga_dev.data_base_addr); + printk(KERN_INFO "FPGA VERSION : %8.8x\n", fpga_version); + if ((err = fpgafw_init()) < 0) { + goto pci_unmap; + } + ds3000_dev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); + platform_driver_register(&ds3000_drv); + return 0; + +pci_unmap: + pci_iounmap(pdev, fpga_dev.data_base_addr); +pci_release: + pci_release_regions(pdev); +pci_disable: + pci_disable_device(pdev); + return -EBUSY; +} + +static void fpga_pci_remove(struct pci_dev *pdev) +{ + platform_driver_unregister(&ds3000_drv); + platform_device_unregister(ds3000_dev); + fpgafw_exit(); + pci_iounmap(pdev, fpga_dev.data_base_addr); + pci_release_regions(pdev); + pci_disable_device(pdev); + printk(KERN_INFO "FPGA PCIe driver remove OK.\n"); +}; + +static struct pci_driver pci_dev_ops = { + .name = FPGA_PCI_NAME, + .probe = fpga_pci_probe, + .remove = fpga_pci_remove, + .id_table = fpga_id_table, +}; + +enum { + READREG, + WRITEREG +}; + +struct fpga_reg_data { + uint32_t addr; + uint32_t value; +}; + +static long fpgafw_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { + int ret = 0; + struct fpga_reg_data data; + mutex_lock(&fpga_data->fpga_lock); + +#ifdef TEST_MODE + static uint32_t status_reg; +#endif + // Switch function to read and write. + switch (cmd) { + case READREG: + if (copy_from_user(&data, (void __user*)arg, sizeof(data)) != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EFAULT; + } + data.value = ioread32(fpga_dev.data_base_addr + data.addr); + if (copy_to_user((void __user*)arg , &data, sizeof(data)) != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EFAULT; + } +#ifdef TEST_MODE + if (data.addr == 0x1210) { + switch (status_reg) { + case 0x0000 : status_reg = 0x8000; + break; + + case 0x8080 : status_reg = 0x80C0; + break; + case 0x80C0 : status_reg = 0x80F0; + break; + case 0x80F0 : status_reg = 0x80F8; + break; + + } + iowrite32(status_reg, fpga_dev.data_base_addr + 0x1210); + } +#endif + + + break; + case WRITEREG: + if (copy_from_user(&data, (void __user*)arg, sizeof(data)) != 0) { + mutex_unlock(&fpga_data->fpga_lock); + return -EFAULT; + } + iowrite32(data.value, fpga_dev.data_base_addr + data.addr); + +#ifdef TEST_MODE + if (data.addr == 0x1204) { + status_reg = 0x8080; + iowrite32(status_reg, fpga_dev.data_base_addr + 0x1210); + } +#endif + + break; + default: + mutex_unlock(&fpga_data->fpga_lock); + return -EINVAL; + } + mutex_unlock(&fpga_data->fpga_lock); + return ret; +} + + +const struct file_operations fpgafw_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = fpgafw_unlocked_ioctl, +}; + + +static int fpgafw_init(void) { + printk(KERN_INFO "Initializing the switchboard driver\n"); + // Try to dynamically allocate a major number for the device -- more difficult but worth it + majorNumber = register_chrdev(0, DEVICE_NAME, &fpgafw_fops); + if (majorNumber < 0) { + printk(KERN_ALERT "Failed to register a major number\n"); + return majorNumber; + } + printk(KERN_INFO "Device registered correctly with major number %d\n", majorNumber); + + // Register the device class + fpgafwclass = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(fpgafwclass)) { // Check for error and clean up if there is + unregister_chrdev(majorNumber, DEVICE_NAME); + printk(KERN_ALERT "Failed to register device class\n"); + return PTR_ERR(fpgafwclass); + } + printk(KERN_INFO "Device class registered correctly\n"); + + // Register the device driver + fpgafwdev = device_create(fpgafwclass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME); + if (IS_ERR(fpgafwdev)) { // Clean up if there is an error + class_destroy(fpgafwclass); // Repeated code but the alternative is goto statements + unregister_chrdev(majorNumber, DEVICE_NAME); + printk(KERN_ALERT "Failed to create the FW upgrade device node\n"); + return PTR_ERR(fpgafwdev); + } + printk(KERN_INFO "FPGA fw upgrade device node created correctly\n"); // Made it! device was initialized + return 0; +} + +static void fpgafw_exit(void) { + device_destroy(fpgafwclass, MKDEV(majorNumber, 0)); // remove the device + class_destroy(fpgafwclass); // remove the device class + unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number + printk(KERN_INFO "Goodbye!\n"); +} + +int ds3000_init(void) +{ + int rc; + rc = pci_register_driver(&pci_dev_ops); + if (rc) + return rc; + return 0; +} + +void ds3000_exit(void) +{ + pci_unregister_driver(&pci_dev_ops); +} + +module_init(ds3000_init); +module_exit(ds3000_exit); + +MODULE_AUTHOR("Pradchaya P. "); +MODULE_DESCRIPTION("Celestica ds3000 switchboard driver"); +MODULE_VERSION(MOD_VERSION); +MODULE_LICENSE("GPL"); diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/setup.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/setup.py new file mode 100644 index 000000000000..a1535165bb66 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/setup.py @@ -0,0 +1,28 @@ +import os +import sys +from setuptools import setup +os.listdir + +setup( + name='sonic-platform', + version='1.0', + description='SONiC platform API implementation on Celestica Platforms based on PDDF', + license='Apache 2.0', + author='SONiC Team', + author_email='linuxnetdev@microsoft.com', + url='https://github.com/Azure/sonic-buildimage', + packages=['sonic_platform'], + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: Plugins', + 'Intended Audience :: Developers', + 'Intended Audience :: Information Technology', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Natural Language :: English', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python :: 3.7', + 'Topic :: Utilities', + ], + keywords='sonic SONiC platform PLATFORM', +) diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/__init__.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/__init__.py new file mode 100644 index 000000000000..21d9cd445e31 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/__init__.py @@ -0,0 +1,4 @@ +# All the derived classes for PDDF +__all__ = ["platform", "chassis", "sfp", "psu", "thermal"] +from . import platform +from . import chassis diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/chassis.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/chassis.py new file mode 100644 index 000000000000..ec8b8ffd10d3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/chassis.py @@ -0,0 +1,283 @@ +############################################################################# +# PDDF +# Module contains an implementation of SONiC Chassis API +# +############################################################################# + +try: + import os + import subprocess + from .event import XcvrEvent + from .helper import APIHelper + from .thermal import ThermalMon, THERMAL_MONITOR_SENSORS + from sonic_py_common import logger + from sonic_platform_pddf_base.pddf_chassis import PddfChassis +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +RESET_SOURCE_BIOS_REG = '0xa107' +LPC_SYSLED_REG = '0xa162' +LPC_GETREG_PATH = "/sys/bus/platform/devices/baseboard/getreg" +LPC_SETREG_PATH = "/sys/bus/platform/devices/baseboard/setreg" +LED_CTRL_MODE_GET_CMD = "ipmitool raw 0x3a 0x42 0x01" + +SYSLOG_IDENTIFIER = "Chassis" +helper_logger = logger.Logger(SYSLOG_IDENTIFIER) + +class Chassis(PddfChassis): + """ + PDDF Platform-specific Chassis class + """ + SYSLED_COLOR_VAL_MAP = { + 'off': '0xff', + 'green': '0xdc', + 'amber': '0xec', + 'amber_blink': '0xee', + 'amber_blink_4hz': '0xee', + 'amber_blink_1hz': '0xed', + 'green_blink': '0xde', + 'green_blink_4hz': '0xde', + 'green_blink_1hz': '0xdd' + } + + SYSLED_VAL_COLOR_MAP = { + '0xff': 'off', + '0xdc': 'green', + '0xec': 'amber', + '0xee': 'amber_blink_4hz', + '0xed': 'amber_blink_1hz', + '0xde': 'green_blink_4hz', + '0xdd': 'green_blink_1hz' + } + + def __init__(self, pddf_data=None, pddf_plugin_data=None): + PddfChassis.__init__(self, pddf_data, pddf_plugin_data) + self._api_helper = APIHelper() + if not self._api_helper.is_bmc_present(): + thermal_count = len(self._thermal_list) + for idx, name in enumerate(THERMAL_MONITOR_SENSORS): + thermal = ThermalMon(thermal_count + idx, name) + self._thermal_list.append(thermal) + + try: + from sonic_platform_pddf_base.pddf_component import PddfComponent # noqa + except ImportError: + # Use custom Component implementation if no pddf_component support + from sonic_platform.custom_component import Component + if self._api_helper.is_bmc_present(): + NUM_COMPONENT = 9 + else: + NUM_COMPONENT = 8 + for i in range(0, NUM_COMPONENT): + component = Component(i) + self._component_list.append(component) + + def initizalize_system_led(self): + """ + This function is not defined in chassis base class, + system-health command would invoke chassis.initizalize_system_led(), + add this stub function just to let the command sucessfully execute + """ + pass + + def get_status_led(self): + """ + Gets the state of the system LED + Args: + None + Returns: + A string, one of the valid LED color strings which could be vendor + specified. + """ + led_status = self._api_helper.lpc_getreg(LPC_GETREG_PATH, LPC_SYSLED_REG) + color = self.SYSLED_VAL_COLOR_MAP.get(led_status, 'unknown') + return color + + def set_status_led(self, color): + """ + Sets the state of the system LED + Args: + color: A string representing the color with which to set the + system LED + Returns: + bool: True if system LED state is set successfully, False if not + """ + if self._api_helper.is_bmc_present(): + led_mode_cmd = LED_CTRL_MODE_GET_CMD + if os.getuid() != 0: + cmd = "sudo " + cmd + led_mode_cmd = "sudo " + led_mode_cmd + status, mode = self._api_helper.get_cmd_output(led_mode_cmd) + # led take automatic control mode, led not settable + if status != 0 or mode.strip() == "01": + helper_logger.log_info("SYS LED takes automatic ctrl mode!") + return False + + # Set SYS_LED through baseboard cpld + color_val = self.SYSLED_COLOR_VAL_MAP.get(color, None) + if color_val == None: + helper_logger.log_error("SYS LED color {} not support!".format(color)) + return False + + status = self._api_helper.lpc_setreg(LPC_SETREG_PATH, LPC_SYSLED_REG, color_val) + + return status + + def get_sfp(self, index): + """ + Retrieves sfp represented by (1-based) index + For Quanta the index in sfputil.py starts from 1, so override + Args: + index: An integer, the index (1-based) of the sfp to retrieve. + The index should be the sequence of a physical port in a chassis, + starting from 1. + Returns: + An object dervied from SfpBase representing the specified sfp + """ + sfp = None + + try: + if (index == 0): + raise IndexError + sfp = self._sfp_list[index-1] + except IndexError: + sys.stderr.write("override: SFP index {} out of range (1-{})\n".format( + index, len(self._sfp_list))) + + return sfp + # Provide the functions/variables below for which implementation is to be overwritten + + def get_reboot_cause(self): + """ + Retrieves the cause of the previous reboot + Returns: + A tuple (string, string) where the first element is a string + containing the cause of the previous reboot. This string must be + one of the predefined strings in this class. If the first string + is "REBOOT_CAUSE_HARDWARE_OTHER", the second string can be used + to pass a description of the reboot cause. + """ + hw_reboot_cause = self._api_helper.lpc_getreg(LPC_GETREG_PATH, RESET_SOURCE_BIOS_REG) + + if hw_reboot_cause == "0x77": + reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE + description = 'Power Cycle Reset' + elif hw_reboot_cause == "0x66": + reboot_cause = self.REBOOT_CAUSE_WATCHDOG + description = 'Hardware Watchdog Reset' + elif hw_reboot_cause == "0x44": + reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE + description = 'CPU Warm Reset' + elif hw_reboot_cause == "0x33": + reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE + description = 'Soft-Set Cold Reset' + elif hw_reboot_cause == "0x22": + reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE + description = 'Soft-Set Warm Reset' + elif hw_reboot_cause == "0x11": + reboot_cause = self.REBOOT_CAUSE_POWER_LOSS + description = 'Power Off Reset' + elif hw_reboot_cause == "0x00": + reboot_cause = self.REBOOT_CAUSE_POWER_LOSS + description = 'Power Cycle Reset' + else: + reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER + description = 'Hardware reason' + + return (reboot_cause, description) + + def get_watchdog(self): + """ + Retreives hardware watchdog device on this chassis + + Returns: + An object derived from WatchdogBase representing the hardware + watchdog device + """ + try: + if self._watchdog is None: + from sonic_platform.cpld_watchdog import Watchdog + # Create the watchdog Instance from cpld watchdog + self._watchdog = Watchdog() + + except Exception as e: + helper_logger.log_error("Fail to load watchdog due to {}".format(e)) + return self._watchdog + + ############################################################## + ###################### Event methods ######################### + ############################################################## + def get_change_event(self, timeout=0): + """ + Returns a nested dictionary containing all devices which have + experienced a change at chassis level + Args: + timeout: Timeout in milliseconds (optional). If timeout == 0, + this method will block until a change is detected. + Returns: + (bool, dict): + - True if call successful, False if not; + - A nested dictionary where key is a device type, + value is a dictionary with key:value pairs in the format of + {'device_id':'device_event'}, + where device_id is the device ID for this device and + device_event, + status='1' represents device inserted, + status='0' represents device removed. + Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0'}} + indicates that fan 0 has been removed, fan 2 + has been inserted and sfp 11 has been removed. + """ + # SFP event + if self.get_num_sfps() == 0: + for index in range(self.platform_inventory['num_ports']): + sfp = Sfp(index, self.pddf_obj, self.plugin_data) + self._sfp_list.append(sfp) + + succeed, sfp_event = XcvrEvent(self._sfp_list).get_xcvr_event(timeout) + if succeed: + return True, {'sfp': sfp_event} + + return False, {'sfp': {}} + + def get_serial(self): + """ + Retrieves the serial number of the chassis (Service tag) + Returns: + string: Serial number of chassis + """ + return self._eeprom.serial_number_str() + + def get_revision(self): + """ + Retrieves the hardware revision for the chassis + Returns: + A string containing the hardware revision for this chassis. + """ + return self._eeprom.revision_str() + + def get_system_airflow(self): + """ + Retrieve system airflow + Returns: + string: INTAKE or EXHAUST + """ + airflow = self.get_serial()[5:8] + if airflow == "B2F": + return "INTAKE" + elif airflow == "F2B": + return "EXHAUST" + return "Unknown" + + def get_thermal_manager(self): + """ + Retrieves thermal manager class on this chasssis + + Returns: + A class derived from ThermalManagerBase representing the + specified thermal manager + """ + if not self._api_helper.is_bmc_present(): + from .thermal_manager import ThermalManager + return ThermalManager + return None diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/component.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/component.py new file mode 100644 index 000000000000..9408cfd5fad4 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/component.py @@ -0,0 +1,146 @@ +try: + import subprocess + from .helper import APIHelper + from sonic_platform_pddf_base.pddf_component import PddfComponent +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +class Component(PddfComponent): + + def __init__(self, component_index, pddf_data=None, pddf_plugin_data=None): + PddfComponent.__init__(self, component_index, pddf_data, pddf_plugin_data) + self._api_helper = APIHelper() + + def get_available_firmware_version(self, image_path): + """ + Retrieves the available firmware version of the component + + Note: the firmware version will be read from image + + Args: + image_path: A string, path to firmware image + + Returns: + A string containing the available firmware version of the component + """ + raise NotImplementedError + + def get_firmware_update_notification(self, image_path): + """ + Retrieves a notification on what should be done in order to complete + the component firmware update + + Args: + image_path: A string, path to firmware image + + Returns: + A string containing the component firmware update notification if required. + By default 'None' value will be used, which indicates that no actions are required + """ + type = self.get_type() + if type == 'bios': + return "BIOS will be updated, please wait for completion and reboot the device to take effect!" + elif type == 'cpld' or type == 'fpga': + return "{} will be updated, please wait for completion and power reboot device to take effect!".format(type.upper()) + elif type == 'bmc': + return "BMC image will be updated, please wait for completion!" + return None + + def install_firmware(self, image_path): + """ + Installs firmware to the component + + This API performs firmware installation only: this may/may not be the same as firmware update. + In case platform component requires some extra steps (apart from calling Low Level Utility) + to load the installed firmware (e.g, reboot, power cycle, etc.) - this must be done manually by user + + Note: in case immediate actions are required to complete the component firmware update + (e.g., reboot, power cycle, etc.) - will be done automatically by API and no return value provided + + Args: + image_path: A string, path to firmware image + + Returns: + A boolean, True if install was successful, False if not + """ + if self.component_data == None: + self.component_data = self._get_component_data() + pre_cmd = self.component_data['pre-update'] + update_cmd = self.component_data['update'] + post_cmd = self.component_data['post-update'] + if pre_cmd != None: + status, _ = self._api_helper.get_cmd_output(pre_cmd) + if status != 0: + return False + if update_cmd != None: + update_cmd = update_cmd.format(image_path) + status, _ = self._api_helper.get_cmd_output(update_cmd) + if status != 0: + return False + if post_cmd != None: + status, _ = self._api_helper.get_cmd_output(post_cmd) + if status != 0: + return False + + return True + + def update_firmware(self, image_path): + """ + Updates firmware of the component + + This API performs firmware update: it assumes firmware installation and loading in a single call. + In case platform component requires some extra steps (apart from calling Low Level Utility) + to load the installed firmware (e.g, reboot, power cycle, etc.) - this will be done automatically by API + + Args: + image_path: A string, path to firmware image + + Returns: + Boolean False if image_path doesn't exist instead of throwing an exception error + Nothing when the update is successful + + Raises: + RuntimeError: update failed + """ + status = self.install_firmware(image_path) + if not status: + return status + + type = self.get_type() + if type == 'fpga' or type == 'cpld': + # TODO:: power cycle FPGA or CPLD + pass + + return True + + def auto_update_firmware(self, image_path, boot_type): + """ + Updates firmware of the component + + This API performs firmware update automatically based on boot_type: it assumes firmware installation + and/or creating a loading task during the reboot, if needed, in a single call. + In case platform component requires some extra steps (apart from calling Low Level Utility) + to load the installed firmware (e.g, reboot, power cycle, etc.) - this will be done automatically during the reboot. + The loading task will be created by API. + + Args: + image_path: A string, path to firmware image + boot_type: A string, reboot type following the upgrade + - none/fast/warm/cold + + Returns: + Output: A return code + return_code: An integer number, status of component firmware auto-update + - return code of a positive number indicates successful auto-update + - status_installed = 1 + - status_updated = 2 + - status_scheduled = 3 + - return_code of a negative number indicates failed auto-update + - status_err_boot_type = -1 + - status_err_image = -2 + - status_err_unknown = -3 + + Raises: + RuntimeError: auto-update failure cause + """ + raise NotImplementedError diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/cpld_watchdog.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/cpld_watchdog.py new file mode 100644 index 000000000000..7889ce15f83f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/cpld_watchdog.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python + +############################################################################# +# +# Watchdog contains an implementation of SONiC Platform Base Watchdog API +# +############################################################################# +try: + import ctypes + import fcntl + import os + import subprocess + import time + import array + import syslog + from .helper import APIHelper + from sonic_platform_base.watchdog_base import WatchdogBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +LPC_CPLD_GETREG_PATH = "/sys/bus/platform/devices/baseboard/getreg" +LPC_CPLD_SETREG_PATH = "/sys/bus/platform/devices/baseboard/setreg" +LPC_WDT_SET_TIMER_L_REG = '0xa183' +LPC_WDT_SET_TIMER_M_REG = '0xa182' +LPC_WDT_SET_TIMER_H_REG = '0xa181' +LPC_WDT_TIMER_L_REG = '0xa186' +LPC_WDT_TIMER_M_REG = '0xa185' +LPC_WDT_TIMER_H_REG = '0xa184' +LPC_WDT_CTRL_REG = '0xa187' +LPC_WDT_ARM_REG = '0xa188' + +WDT_ENABLE = 0x1 +WDT_DISABLE = 0x0 +WDT_COMMON_ERROR = -1 +DEFAULT_TIMEOUT = 180 + +class CpldWatchdog(WatchdogBase): + + def __init__(self): + WatchdogBase.__init__(self) + # Set default value + self._api_helper = APIHelper() + self._ka_count = int(1) + self.armed = True if self._active() else False + self.timeout = self._gettimeout() if self.armed else DEFAULT_TIMEOUT + #self._disable() + + def _lpc_get(self, reg): + return self._api_helper.lpc_getreg(LPC_CPLD_GETREG_PATH, reg) + + def _lpc_set(self, reg, val): + if type(val) is int: + val = hex(val) + return self._api_helper.lpc_setreg(LPC_CPLD_SETREG_PATH, reg, val) + + def _active(self): + """ + WDT is active or not + """ + data = self._lpc_get(LPC_WDT_CTRL_REG) + return True if data == "0x01" else False + + def _enable(self): + """ + Turn on the watchdog timer + """ + status = self._lpc_set(LPC_WDT_CTRL_REG, WDT_ENABLE) + if not status: + pass + + def _disable(self): + """ + Turn off the watchdog timer + """ + status = self._lpc_set(LPC_WDT_CTRL_REG, WDT_DISABLE) + if not status: + pass + + def _keepalive(self): + """ + Keep alive watchdog timer + """ + if bool(self._ka_count % 2): + status = self._lpc_set(LPC_WDT_ARM_REG, WDT_ENABLE) + else: + status = self._lpc_set(LPC_WDT_ARM_REG, WDT_DISABLE) + + if not status: + syslog.syslog(syslog.LOG_ERR, "Feed Watchdog failed") + + self._ka_count = self._ka_count + 1 + if (self._ka_count >= 11): + self._ka_count = 1 + + def _settimeout(self, seconds): + """ + Set watchdog timer timeout + @param seconds - timeout in seconds + @return is the actual set timeout + """ + ms = seconds * 1000 + ms_low_byte = ms & 0xff + ms_media_byte = (ms >> 8) & 0xff + ms_high_byte = (ms >> 16) & 0xff + self._lpc_set(LPC_WDT_SET_TIMER_L_REG, ms_low_byte) + self._lpc_set(LPC_WDT_SET_TIMER_M_REG, ms_media_byte) + self._lpc_set(LPC_WDT_SET_TIMER_H_REG, ms_high_byte) + return self._gettimeout() + + def _gettimeout(self): + """ + Get watchdog timeout + @return watchdog timeout + """ + data = [0, 0, 0] + data[0] = self._lpc_get(LPC_WDT_SET_TIMER_L_REG) + data[1] = self._lpc_get(LPC_WDT_SET_TIMER_M_REG) + data[2] = self._lpc_get(LPC_WDT_SET_TIMER_H_REG) + seconds = int((int(data[2], 16) << 16 + | int(data[1], 16) << 8 + | int(data[0], 16)) / 1000) + return seconds + + def _gettimeleft(self): + """ + Get time left before watchdog timer expires + @return time left in seconds + """ + data = [0, 0, 0] + data[0] = self._lpc_get(LPC_WDT_TIMER_L_REG) + data[1] = self._lpc_get(LPC_WDT_TIMER_M_REG) + data[2] = self._lpc_get(LPC_WDT_TIMER_H_REG) + seconds = int((int(data[2], 16) << 16 + | int(data[1], 16) << 8 + | int(data[0], 16)) / 1000) + + return seconds + + ################################################################# + + def arm(self, seconds): + """ + Arm the hardware watchdog with a timeout of seconds. + If the watchdog is currently armed, calling this function will + simply reset the timer to the provided value. If the underlying + hardware does not support the value provided in , this + method should arm the watchdog with the *next greater* available + value. + Returns: + An integer specifying the *actual* number of seconds the watchdog + was armed with. On failure returns -1. + """ + + ret = WDT_COMMON_ERROR + if seconds < 0: + return ret + + try: + if self.armed: + self._keepalive() + if self.timeout != seconds: + self._disable() + time.sleep(1) + self.timeout = self._settimeout(seconds) + self._enable() + else: + self.timeout = self._settimeout(seconds) + self._keepalive() + self._enable() + self.armed = True + ret = self.timeout + except IOError as e: + pass + + return ret + + def disarm(self): + """ + Disarm the hardware watchdog + Returns: + A boolean, True if watchdog is disarmed successfully, False if not + """ + disarmed = False + if self.is_armed(): + try: + self._disable() + self.armed = False + disarmed = True + except IOError: + pass + + return disarmed + + def is_armed(self): + """ + Retrieves the armed state of the hardware watchdog. + Returns: + A boolean, True if watchdog is armed, False if not + """ + + return self.armed + + def get_remaining_time(self): + """ + If the watchdog is armed, retrieve the number of seconds remaining on + the watchdog timer + Returns: + An integer specifying the number of seconds remaining on thei + watchdog timer. If the watchdog is not armed, returns -1. + """ + + timeleft = WDT_COMMON_ERROR + + if self.armed: + try: + timeleft = self._gettimeleft() + except IOError: + pass + + return timeleft + +class Watchdog(CpldWatchdog): + """PDDF Platform-Specific Watchdog Class""" + + def __init__(self): + CpldWatchdog.__init__(self) + + # Provide the functions/variables below for which implementation is to be overwritten diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/custom_component.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/custom_component.py new file mode 100644 index 000000000000..67f234ef6946 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/custom_component.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Component contains an implementation of SONiC Platform Base API and +# provides the components firmware management function +# +############################################################################# + +import subprocess +import re + +try: + from sonic_platform_base.component_base import ComponentBase + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +COMPONENT_LIST_BMC = [ + ("BIOS", "Basic input/output System"), + ("BMC", "Baseboard Management Controller"), + ("BaseBoard_CPLD", "Base board CPLD"), + ("SwitchBoard_CPLD1", "Switch board CPLD 1"), + ("SwitchBoard_CPLD2", "Switch board CPLD 2"), + ("COMeBoard_CPLD", "COMe board CPLD"), + ("FPGA", "Baseboard FPGA"), + ("PCIe", "ASIC PCIe Firmware"), + ("SSD", "Solid State Drive firmware version") +] + +COMPONENT_LIST_NONBMC = [ + ("BIOS", "Basic input/output System"), + ("BaseBoard_CPLD", "Base board CPLD"), + ("SwitchBoard_CPLD1", "Switch board CPLD 1"), + ("SwitchBoard_CPLD2", "Switch board CPLD 2"), + ("COMeBoard_CPLD", "COMe board CPLD"), + ("FPGA", "Baseboard FPGA"), + ("PCIe", "ASIC PCIe Firmware"), + ("SSD", "Solid State Drive firmware version") +] + +NAME_INDEX = 0 +DESCRIPTION_INDEX = 1 + +BIOS_VERSION_CMD = "dmidecode -s bios-version" +BASECPLD_VERSION_CMD="cat /sys/devices/platform/baseboard/version" +SWCPLD1_VERSION_CMD = "i2cget -y -f 102 0x30 0x0" +SWCPLD2_VERSION_CMD = "i2cget -y -f 102 0x31 0x0" +COMECPLD_VERSION_CMD="cat /sys/devices/platform/baseboard/come_cpld_version" +FPGA_VERSION_CMD="cat /sys/devices/platform/fpga_sysfs/version" +ASIC_PCIE_VERSION_CMD = "bcmcmd 'pciephy fw version' | grep 'PCIe FW version' | cut -d ' ' -f 4" +SSD_VERSION_CMD = "smartctl -i /dev/sda" + +UNKNOWN_VER = "unknown" + +class Component(): + """Platform-specific Component class""" + + DEVICE_TYPE = "component" + + def __init__(self, component_index): + ComponentBase.__init__(self) + self._api_helper = APIHelper() + self.index = component_index + self.name = self.get_name() + + def _get_asic_pcie_ver(self): + status, raw_ver=self.run_command(ASIC_PCIE_VERSION_CMD) + if status: + return raw_ver + else: + return UNKNOWN_VER + + def _get_bios_ver(self): + status, raw_ver=self.run_command(BIOS_VERSION_CMD) + if status: + return raw_ver + else: + return UNKNOWN_VER + + def _get_basecpld_ver(self): + status, raw_ver=self.run_command(BASECPLD_VERSION_CMD) + if status: + ver_str = "{}.{}".format(int(raw_ver[2], 16), int(raw_ver[3], 16)) + return ver_str + else: + return UNKNOWN_VER + + def _get_swcpld1_ver(self): + status, raw_ver=self.run_command(SWCPLD1_VERSION_CMD) + if status: + ver_str = "{}.{}".format(int(raw_ver[2], 16), int(raw_ver[3], 16)) + return ver_str + else: + return UNKNOWN_VER + + def _get_swcpld2_ver(self): + status, raw_ver=self.run_command(SWCPLD2_VERSION_CMD) + if status: + ver_str = "{}.{}".format(int(raw_ver[2], 16), int(raw_ver[3], 16)) + return ver_str + else: + return UNKNOWN_VER + + def _get_comecpld_ver(self): + status, raw_ver=self.run_command(COMECPLD_VERSION_CMD) + if status: + ver_str = "{}.{}".format(int(raw_ver[2], 16), int(raw_ver[3], 16)) + return ver_str + else: + return UNKNOWN_VER + + def _get_fpga_ver(self): + status, raw_ver=self.run_command(FPGA_VERSION_CMD) + if status: + iver = int(raw_ver, 16) + ver = "{}.{}".format(iver >> 16, iver & 0xffff) + return ver + else: + return UNKNOWN_VER + + def _get_bmc_ver(self): + cmd="ipmitool mc info | grep 'Firmware Revision'" + status, raw_ver=self.run_command(cmd) + if status: + bmc_ver=raw_ver.split(':')[-1].strip() + return bmc_ver + else: + return UNKNOWN_VER + + def _get_ssd_ver(self): + ssd_ver = "N/A" + status, raw_ssd_data = self.run_command(SSD_VERSION_CMD) + if status: + ret = re.search(r"Firmware Version: +(.*)[^\\]", raw_ssd_data) + if ret != None: + ssd_ver = ret.group(1) + return ssd_ver + + def get_name(self): + """ + Retrieves the name of the component + Returns: + A string containing the name of the component + """ + if self._api_helper.is_bmc_present(): + return COMPONENT_LIST_BMC[self.index][NAME_INDEX] + else: + return COMPONENT_LIST_NONBMC[self.index][NAME_INDEX] + + def get_description(self): + """ + Retrieves the description of the component + Returns: + A string containing the description of the component + """ + if self._api_helper.is_bmc_present(): + return COMPONENT_LIST_BMC[self.index][DESCRIPTION_INDEX] + else: + return COMPONENT_LIST_NONBMC[self.index][DESCRIPTION_INDEX] + + def get_firmware_version(self): + """ + Retrieves the firmware version of module + Returns: + string: The firmware versions of the module + """ + fw_func_map = { + "BIOS": "_get_bios_ver", + "BMC": "_get_bmc_ver", + "BaseBoard_CPLD": "_get_basecpld_ver", + "SwitchBoard_CPLD1": "_get_swcpld1_ver", + "SwitchBoard_CPLD2": "_get_swcpld2_ver", + "COMeBoard_CPLD": "_get_comecpld_ver", + "FPGA": "_get_fpga_ver", + "PCIe": "_get_asic_pcie_ver", + "SSD": "_get_ssd_ver" + } + fw_method = getattr(self, fw_func_map[self.name], None) + if fw_method: + fw_ver = fw_method() + return fw_ver + else: + return UNKNOWN_VER + + def run_command(self, cmd): + status = True + result = "" + try: + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err.decode('UTF-8') == '': + result = raw_data.strip().decode('UTF-8') + except Exception: + status = False + return status, result diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/eeprom.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/eeprom.py new file mode 100644 index 000000000000..139f8641428f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/eeprom.py @@ -0,0 +1,75 @@ +try: + from sonic_platform_pddf_base.pddf_eeprom import PddfEeprom +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Eeprom(PddfEeprom): + + _TLV_DISPLAY_VENDOR_EXT = True + _TLV_INFO_MAX_LEN = 256 + pddf_obj = {} + plugin_data = {} + + def __init__(self, pddf_data=None, pddf_plugin_data=None): + if not pddf_data or not pddf_plugin_data: + raise ValueError('PDDF JSON data error') + + self.pddf_obj = pddf_data + self.plugin_data = pddf_plugin_data + + # system EEPROM always has device name EEPROM1 + self.eeprom_path = self.pddf_obj.get_path("EEPROM1", "eeprom") + if self.eeprom_path is None: + return + + super(PddfEeprom, self).__init__(self.eeprom_path, 0, '', True) + self.eeprom_tlv_dict = dict() + try: + self.eeprom_data = self.read_eeprom() + except Exception as e: + self.eeprom_data = "N/A" + raise RuntimeError("PddfEeprom is not Programmed - Error: {}".format(str(e))) + else: + 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) < self._TLV_INFO_MAX_LEN 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])) + + if (tlv[0]) == self._TLV_CODE_VENDOR_EXT: + name = "Vendor Extension" + value = "" + if self._TLV_DISPLAY_VENDOR_EXT: + for c in tlv[2:2 + tlv[1]]: + value += "0x%02X " % c + else: + 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 + + + # Provide the functions/variables below for which implementation is to be overwritten + def revision_str(self): + (is_valid, results) = self.get_tlv_field(self.eeprom_data, self._TLV_CODE_DEVICE_VERSION) + if not is_valid: + "N/A" + if type(results[2]) is bytearray: + return str(int.from_bytes(results[2], byteorder='little')) + + return results[2].decode('ascii') diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/event.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/event.py new file mode 100644 index 000000000000..86a1390cd6b7 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/event.py @@ -0,0 +1,52 @@ +try: + import time + from sonic_py_common.logger import Logger +except ImportError as e: + raise ImportError(repr(e) + " - required module not found") + +class XcvrEvent: + ''' Listen to insert/remove QSFP/SFP+ events ''' + + def __init__(self, sfp_list): + self._sfp_list = sfp_list + self._logger = Logger() + + xcvr_change_event_data = {'valid': 0, 'last': 0, 'present': 0} + + def get_xcvr_event(self, timeout): + port_dict = {} + + # Using polling mode + now = time.time() + + if timeout < 1000: + timeout = 1000 + timeout = timeout / float(1000) # Convert to secs + + if now < (self.xcvr_change_event_data['last'] + timeout) \ + and self.xcvr_change_event_data['valid']: + return True, port_dict + + bitmap = 0 + for sfp in self._sfp_list: + modpres = sfp.get_presence() + index = sfp.port_index - 1 + if modpres: + bitmap = bitmap | (1 << index) + + changed_ports = self.xcvr_change_event_data['present'] ^ bitmap + if changed_ports: + for sfp in self._sfp_list: + index = sfp.port_index - 1 + if changed_ports & (1 << index): + if (bitmap & (1 << index)) == 0: + port_dict[str(index + 1)] = '0' + else: + port_dict[str(index + 1)] = '1' + + # Update the cache dict + self.xcvr_change_event_data['present'] = bitmap + self.xcvr_change_event_data['last'] = now + self.xcvr_change_event_data['valid'] = 1 + + return True, port_dict diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/fan.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/fan.py new file mode 100644 index 000000000000..38039c03a210 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/fan.py @@ -0,0 +1,172 @@ +import os + +try: + from .helper import APIHelper + from sonic_platform_pddf_base.pddf_fan import PddfFan +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Fan(PddfFan): + """PDDF Platform-Specific Fan class""" + + BMC_FAN_FSC_STATUS_CMD = "ipmitool raw 0x3a 0x26 0x00" + LPC_CPLD_SETREG_PATH = "/sys/bus/platform/devices/baseboard/setreg" + FAN_PWM_CTRL_REG_MAP = { + 1: '0xa1b2', + 2: '0xa1b8', + 3: '0xa1c4', + 4: '0xa1ca' + } + + def __init__(self, tray_idx, fan_idx=0, pddf_data=None, pddf_plugin_data=None, is_psu_fan=False, psu_index=0): + # idx is 0-based + PddfFan.__init__(self, tray_idx, fan_idx, pddf_data, pddf_plugin_data, is_psu_fan, psu_index) + self._api_helper = APIHelper() + + def get_presence(self): + """ + Retrieves the presence of fan + """ + if self.is_psu_fan: + return super().get_presence() + return super().get_presence() and self.get_status() + + def get_direction(self): + """ + Retrieves the direction of fan + + Returns: + A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST + depending on fan direction + Or N/A if fan removed or abnormal + """ + if not self.get_status(): + return "N/A" + + if self.is_psu_fan: + from sonic_platform.psu import Psu + psu = Psu(self.fans_psu_index - 1, self.pddf_obj, self.plugin_data) + model = psu.get_model() + if model in ["FSP550-20FM", "G1251-0550WNA"]: + return "EXHAUST" + elif model in ["FSP550-29FM", "G1251-0550WRA"]: + return "INTAKE" + return "Unknown" + + return super().get_direction() + + + def get_target_speed(self): + """ + Retrieves the target (expected) speed of the fan + + Returns: + An integer, the percentage of full fan speed, in the range 0 (off) + to 100 (full speed) + """ + target_speed = 0 + if self.is_psu_fan: + # PSU fan not controllable, return current speed + return self.get_speed() + else: + speed_rpm = self.get_speed_rpm() + if self.fan_index == 1: + max_fan_rpm = eval(self.plugin_data['FAN']['FRONT_FAN_MAX_RPM_SPEED']) + else: + max_fan_rpm = eval(self.plugin_data['FAN']['REAR_FAN_MAX_RPM_SPEED']) + speed_percentage = round(int((speed_rpm * 100) / max_fan_rpm)) + target_speed = speed_percentage + + return target_speed + + def get_speed(self): + """ + Retrieves the speed of fan as a percentage of full speed + + Returns: + An integer, the percentage of full fan speed, in the range 0 (off) + to 100 (full speed) + """ + if self.is_psu_fan: + attr = "psu_fan{}_speed_rpm".format(self.fan_index) + device = "PSU{}".format(self.fans_psu_index) + output = self.pddf_obj.get_attr_name_output(device, attr) + if not output: + return 0 + + output['status'] = output['status'].rstrip() + if output['status'].isalpha(): + return 0 + else: + speed = int(float(output['status'])) + + max_speed = int(self.plugin_data['PSU']['PSU_FAN_MAX_SPEED']) + speed_percentage = round((speed*100)/max_speed) + return speed_percentage if speed_percentage <= 100 else 100 + else: + # Get fan rpm instead of fan pwm + idx = (self.fantray_index-1)*self.platform['num_fans_pertray'] + self.fan_index + attr = "fan" + str(idx) + "_input" + output = self.pddf_obj.get_attr_name_output("FAN-CTRL", attr) + + if not output: + return 0 + + output['status'] = output['status'].rstrip() + if output['status'].isalpha(): + return 0 + else: + speed = int(float(output['status'])) + + if self.fan_index == 1: + max_speed = int(self.plugin_data['FAN']['FRONT_FAN_MAX_RPM_SPEED']) + else: + max_speed = int(self.plugin_data['FAN']['REAR_FAN_MAX_RPM_SPEED']) + speed_percentage = round((speed*100)/max_speed) + + return speed_percentage if speed_percentage <= 100 else 100 + + def set_speed(self, speed): + """ + Sets the fan speed + + Args: + speed: An integer, the percentage of full fan speed to set fan to, + in the range 0 (off) to 100 (full speed) + + Returns: + A boolean, True if speed is set successfully, False if not + """ + if self.is_psu_fan: + print("Setting PSU fan speed is not allowed") + return False + + if speed < 0 or speed > 100: + print("Error: Invalid speed %d. Please provide a valid speed percentage" % speed) + return False + + if 'duty_cycle_to_pwm' not in self.plugin_data['FAN']: + print("Setting fan speed is not allowed !") + return False + + duty_cycle_to_pwm = eval(self.plugin_data['FAN']['duty_cycle_to_pwm']) + pwm = int(round(duty_cycle_to_pwm(speed))) + + if self._api_helper.is_bmc_present(): + status, data = self._api_helper.get_cmd_output(self.BMC_FAN_FSC_STATUS_CMD) + if status != 0: + print("Error: failed to get BMC FSC status") + return False + if data == '01': + # Enable BMC FSC mode + return False + + # FAN 1 & 2 in same fantray share the same register, skip Fan2 setting + if self.fan_index == 2: + return True + # Set FAN PWM through baseboard CPLD + reg = self.FAN_PWM_CTRL_REG_MAP.get(self.fantray_index) + status = self._api_helper.lpc_setreg(self.LPC_CPLD_SETREG_PATH, reg, hex(pwm)) + + return status diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/fan_drawer.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/fan_drawer.py new file mode 100644 index 000000000000..3946e2bdec56 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/fan_drawer.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + + +try: + from sonic_platform_pddf_base.pddf_fan_drawer import PddfFanDrawer +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class FanDrawer(PddfFanDrawer): + """PDDF Platform-Specific Fan-Drawer class""" + + def __init__(self, tray_idx, pddf_data=None, pddf_plugin_data=None): + # idx is 0-based + PddfFanDrawer.__init__(self, tray_idx, pddf_data, pddf_plugin_data) + + # Provide the functions/variables below for which implementation is to be overwritten + def get_presence(self): + status = False + # Usually if a tray is removed, all the fans inside it are absent + if self._fan_list and len(self._fan_list) == 2: + status = self._fan_list[0].get_presence() or self._fan_list[1].get_presence() + else: + status = self._fan_list[0].get_presence() + return status diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/helper.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/helper.py new file mode 100644 index 000000000000..25b5d5489a5c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/helper.py @@ -0,0 +1,130 @@ +import fcntl +import os +import struct +import subprocess +from mmap import * + +BMC_PRES_SYS_PATH = '/sys/bus/platform/devices/baseboard/bmc_presence' + +class APIHelper(): + def pci_get_value(self, resource, offset): + status = True + result = "" + try: + fd = os.open(resource, os.O_RDWR) + mm = mmap(fd, 0) + mm.seek(int(offset)) + read_data_stream = mm.read(4) + result = struct.unpack('I', read_data_stream) + except: + status = False + return status, result + + def get_cmd_output(self, cmd): + try: + data = subprocess.check_output(cmd, shell=True, + universal_newlines=True, stderr=subprocess.STDOUT).strip() + status = 0 + except subprocess.CalledProcessError as ex: + data = ex.output + status = ex.returncode + return status, data + + def read_txt_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.read() + return data.strip() + except IOError: + pass + return None + + def read_one_line_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.readline() + return data.strip() + except IOError: + pass + return None + + def write_txt_file(self, file_path, value): + try: + with open(file_path, 'w') as fd: + fd.write(str(value)) + except Exception: + return False + return True + + def lpc_getreg(self, getreg_path, reg): + """ + Get the cpld reg through lpc interface + + Args: + getreg_path: getreg sysfs path + reg: 16 bits reg addr in hex str format + + Returns: + A str, register value in hex str format + """ + file = open(getreg_path, 'w+') + # Acquire an exclusive lock on the file + fcntl.flock(file, fcntl.LOCK_EX) + + try: + file.write(reg) + file.flush() + + # Seek to the beginning of the file + file.seek(0) + + # Read the content of the file + result = file.readline().strip() + finally: + # Release the lock and close the file + fcntl.flock(file, fcntl.LOCK_UN) + file.close() + + return result + + def lpc_setreg(self, setreg_path, reg, val): + """ + Set the cpld reg through lpc interface + + Args: + setreg_path: setreg sysfs path + reg: 16 bits reg addr in hex str format + val: 8 bits register value in hex str format + + Returns: + A boolean, True if speed is set successfully, False if not + """ + status = True + file = open(setreg_path, 'w') + # Acquire an exclusive lock on the file + fcntl.flock(file, fcntl.LOCK_EX) + + try: + data = "{} {}".format(reg, val) + file.write(data) + file.flush() + except: + status = False + finally: + # Release the lock and close the file + fcntl.flock(file, fcntl.LOCK_UN) + file.close() + + return status + + def is_bmc_present(self): + """ + Get the BMC card present status + + Returns: + A boolean, True if present, False if absent + """ + presence = self.read_txt_file(BMC_PRES_SYS_PATH) + if presence == None: + print("Failed to get BMC card presence status") + return True if presence == "present" else False diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/pcie.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/pcie.py new file mode 100644 index 000000000000..6c01e694fdb3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/pcie.py @@ -0,0 +1,10 @@ +try: + from sonic_platform_base.sonic_pcie.pcie_common import PcieUtil +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +class Pcie(PcieUtil): + """Celestica Platform-specific PCIe class""" + + def __init__(self, platform_path): + PcieUtil.__init__(self, platform_path) diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/platform.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/platform.py new file mode 100644 index 000000000000..8595e80692df --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/platform.py @@ -0,0 +1,23 @@ +############################################################################# +# PDDF +# Module contains an implementation of SONiC Platform Base API and +# provides the platform information +# +############################################################################# + + +try: + from sonic_platform_pddf_base.pddf_platform import PddfPlatform +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Platform(PddfPlatform): + """ + PDDF Platform-Specific Platform Class + """ + + def __init__(self): + PddfPlatform.__init__(self) + + # Provide the functions/variables below for which implementation is to be overwritten diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/psu.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/psu.py new file mode 100644 index 000000000000..3cb3bbaec923 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/psu.py @@ -0,0 +1,72 @@ +try: + from sonic_platform_pddf_base.pddf_psu import PddfPsu + from .helper import APIHelper +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + +LPC_GETREG_PATH = "/sys/bus/platform/devices/baseboard/getreg" +LPC_PSU_STATUS_REG = '0xa160' +LPC_PSU_POWER_STATUS_OFFSET = 0 +LPC_PSU_PRES_STATUS_OFFSET = 2 + +class Psu(PddfPsu): + """PDDF Platform-Specific PSU class""" + + def __init__(self, index, pddf_data=None, pddf_plugin_data=None): + PddfPsu.__init__(self, index, pddf_data, pddf_plugin_data) + self._api_helper = APIHelper() + + # Provide the functions/variables below for which implementation is to be overwritten + def get_capacity(self): + return 550 + + def get_type(self): + return 'AC' + + def get_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + psu_status = int(self._api_helper.lpc_getreg(LPC_GETREG_PATH, LPC_PSU_STATUS_REG), 16) + presence = psu_status & (1 << (LPC_PSU_PRES_STATUS_OFFSET + 2 - self.psu_index)) + return True if presence == 0 else False + + def get_status(self): + """ + Retrieves the operational status of the device + + Returns: + A boolean value, True if device is operating properly, False if not + """ + psu_status = int(self._api_helper.lpc_getreg(LPC_GETREG_PATH, LPC_PSU_STATUS_REG), 16) + power_status = psu_status & (1 << (LPC_PSU_POWER_STATUS_OFFSET + 2 - self.psu_index)) + return False if power_status == 0 else True + + def get_revision(self): + """ + Retrieves the revision of the device + Returns: + string: revision of device + """ + if not self.get_presence(): + return 'N/A' + + if self._api_helper.is_bmc_present(): + cmd = "ipmitool fru list {} | grep 'Product Version'".format(5 - self.psu_index) + status, output = self._api_helper.get_cmd_output(cmd) + if status == 0: + rev = output.split()[-1] + return rev + else: + # Get the revision information from FRU + cmd1 = "i2cget -y -f {} {} 0x2d b".format(42 + self.psu_index - 1, hex(0x52 + self.psu_index - 1)) + status1, output1 = self._api_helper.get_cmd_output(cmd1) + cmd2 = "i2cget -y -f {} {} 0x2e b".format(42 + self.psu_index - 1, hex(0x52 + self.psu_index - 1)) + status2, output2 = self._api_helper.get_cmd_output(cmd2) + if status1 == 0 and status2 == 0: + rev = bytes.fromhex(output1[2:] + output2[2:]).decode('utf-8') + return rev + return 'N/A' diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/sfp.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/sfp.py new file mode 100644 index 000000000000..7142a5733d10 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/sfp.py @@ -0,0 +1,94 @@ +try: + import sys + import syslog + import time + from multiprocessing import Lock + from sonic_platform_pddf_base.pddf_sfp import PddfSfp +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + + +class Sfp(PddfSfp): + """ + PDDF Platform-Specific Sfp class + """ + + def __init__(self, index, pddf_data=None, pddf_plugin_data=None): + PddfSfp.__init__(self, index, pddf_data, pddf_plugin_data) + self.eeprom_lock = Lock() + + # Provide the functions/variables below for which implementation is to be overwritten + # Add reties to work around FPGAPCI 0050/eeprom: offset 0x0: sometimes read failed + def __read_eeprom(self, offset, num_bytes): + """ + read eeprom specfic bytes beginning from a random offset with size as num_bytes + + Args: + offset : + Integer, the offset from which the read transaction will start + num_bytes: + Integer, the number of bytes to be read + + Returns: + bytearray, if raw sequence of bytes are read correctly from the offset of size num_bytes + None, if the read_eeprom fails + """ + buf = None + eeprom_raw = [] + sysfs_sfp_i2c_client_eeprom_path = self.eeprom_path + + if not self.get_presence(): + return None + + sysfsfile_eeprom = None + attempts = 0 + max_retries = 5 + success = False + while attempts < max_retries and not success: + try: + if attempts > 0: + time.sleep(0.2) + sysfsfile_eeprom = open(sysfs_sfp_i2c_client_eeprom_path, "rb", 0) + sysfsfile_eeprom.seek(offset) + buf = sysfsfile_eeprom.read(num_bytes) + success = True + except Exception as ex: + attempts += 1 + # Eliminate the redundant errors by showing errors only for lower page and page 0 + if attempts == max_retries: + if offset < 256: + syslog.syslog(syslog.LOG_INFO, "port {0}: {1}: offset {2}: read reach retry limit, refer to last eeprom cache".format(self.port_index, sysfs_sfp_i2c_client_eeprom_path, hex(offset))) + return None + finally: + if sysfsfile_eeprom is not None: + sysfsfile_eeprom.close() + + if buf is None: + return None + + for x in buf: + eeprom_raw.append(x) + + while len(eeprom_raw) < num_bytes: + eeprom_raw.append(0) + return bytes(eeprom_raw) + + # Read out any bytes from any offset + def read_eeprom(self, offset, num_bytes): + """ + read eeprom specfic bytes beginning from a random offset with size as num_bytes + + Args: + offset : + Integer, the offset from which the read transaction will start + num_bytes: + Integer, the number of bytes to be read + + Returns: + bytearray, if raw sequence of bytes are read correctly from the offset of size num_bytes + None, if the read_eeprom fails + """ + self.eeprom_lock.acquire() + bytes = self.__read_eeprom(offset, num_bytes) + self.eeprom_lock.release() + return bytes diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal.py new file mode 100644 index 000000000000..911d2f7dd4b5 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal.py @@ -0,0 +1,98 @@ +try: + from sonic_platform_pddf_base.pddf_thermal import PddfThermal + from sonic_platform_base.thermal_base import ThermalBase + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +SENSORS_LOW_THRESHOLD_MAP = {"Base_Temp_U5": -5, "Base_Temp_U56": -5, + "Switch_Temp_U28": -5, "Switch_Temp_U29": -5} + +class Thermal(PddfThermal): + """PDDF Platform-Specific Thermal class""" + + def __init__(self, index, pddf_data=None, pddf_plugin_data=None, is_psu_thermal=False, psu_index=0): + PddfThermal.__init__(self, index, pddf_data, pddf_plugin_data, is_psu_thermal, psu_index) + + # Provide the functions/variables below for which implementation is to be overwritten + + def get_high_threshold(self): + if self.is_psu_thermal: + device = "PSU{}".format(self.thermals_psu_index) + output = self.pddf_obj.get_attr_name_output(device, "psu_temp1_high_threshold") + if not output: + return None + + temp1 = output['status'] + # temperature returned is in milli celcius + return float(temp1)/1000 + else: + return super().get_high_threshold() + + def get_low_threshold(self): + low_threshold = SENSORS_LOW_THRESHOLD_MAP.get(self.get_name(), None) + if low_threshold != None: + return low_threshold + return super().get_low_threshold() + + +BCM_TEMP_GET_CMD = "cat /sys/devices/platform/fpga_sysfs/bcm_temp" +THERMAL_MONITOR_SENSORS = ["CPU_Temp", "BCM_SW_Temp", "VDD_CORE_Temp", "VDD_ANLG_Temp"] +THERMAL_THRESHOLDS = { "CPU_Temp": { "high_threshold": 89, "low_threshold": 'N/A', "high_crit_threshold": 93, + "temp_cmd": "r=$(cat /sys/class/thermal/thermal_zone1/temp) && printf '%.1f' $(($r / 1000))" }, + "BCM_SW_Temp": { "high_threshold": 110, "low_threshold": 'N/A', "high_crit_threshold": 120, + "temp_cmd": "r=$(cat /sys/devices/platform/fpga_sysfs/bcm_temp) && printf '%.1f' $(((434100 - ((12500000 / $r - 1) * 535) + 5000) / 1000))"}, + "VDD_CORE_Temp": { "high_threshold": 120, "low_threshold": 'N/A', "high_crit_threshold": 'N/A', + "temp_cmd": "r=$(cat /sys/class/hwmon/hwmon45/temp1_input) && printf '%.1f' $(($r / 1000))" }, + "VDD_ANLG_Temp": { "high_threshold": 120, "low_threshold": 'N/A', "high_crit_threshold": 'N/A', + "temp_cmd": "r=$(cat /sys/class/hwmon/hwmon44/temp1_input) && printf '%.1f' $(($r / 1000))" }} + +class ThermalMon(ThermalBase): + def __init__(self, index, name): + self.thermal_index = index + 1 + self.thermal_name = name + self._helper = APIHelper() + + def get_name(self): + return self.thermal_name + + def get_presence(self): + return True + + def get_temperature(self): + if self.get_name() == "BCM_SW_Temp": + status, data = self._helper.get_cmd_output(BCM_TEMP_GET_CMD) + if status != 0: + return 'N/A' + if data.strip() == "0x0000": + return 0.0 + thermal_data = THERMAL_THRESHOLDS.get(self.thermal_name, None) + if thermal_data == None: + return 'N/A' + temp_cmd = thermal_data.get("temp_cmd") + status, data = self._helper.get_cmd_output(temp_cmd) + if status != 0: + return 'N/A' + return float(data) + + def get_high_threshold(self): + thermal_data = THERMAL_THRESHOLDS.get(self.thermal_name, None) + if thermal_data == None: + return 'N/A' + threshold = thermal_data.get("high_threshold", 'N/A') + return float(threshold) if threshold != 'N/A' else 'N/A' + + def get_low_threshold(self): + thermal_data = THERMAL_THRESHOLDS.get(self.thermal_name, None) + if thermal_data == None: + return 'N/A' + threshold = thermal_data.get("low_threshold", 'N/A') + return float(threshold) if threshold != 'N/A' else 'N/A' + + def get_high_critical_threshold(self): + thermal_data = THERMAL_THRESHOLDS.get(self.thermal_name, None) + if thermal_data == None: + return 'N/A' + threshold = thermal_data.get("high_crit_threshold", 'N/A') + return float(threshold) if threshold != 'N/A' else 'N/A' diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_actions.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_actions.py new file mode 100644 index 000000000000..8be043d94310 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_actions.py @@ -0,0 +1,341 @@ +from sonic_platform_base.sonic_thermal_control.thermal_action_base import ThermalPolicyActionBase +from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object +from .helper import APIHelper + +from sonic_py_common import logger + +sonic_logger = logger.Logger('thermal_actions') + + +class SetFanSpeedAction(ThermalPolicyActionBase): + """ + Base thermal action class to set speed for fans + """ + # JSON field definition + JSON_FIELD_SPEED = 'speed' + + def __init__(self): + """ + Constructor of SetFanSpeedAction + """ + self.default_speed = 50 + self.hightemp_speed = 100 + self.speed = self.default_speed + + def load_from_json(self, json_obj): + """ + Construct SetFanSpeedAction via JSON. JSON example: + { + "type": "fan.all.set_speed" + "speed": "100" + } + :param json_obj: A JSON object representing a SetFanSpeedAction action. + :return: + """ + if SetFanSpeedAction.JSON_FIELD_SPEED in json_obj: + speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_SPEED]) + if speed < 0 or speed > 100: + raise ValueError('SetFanSpeedAction invalid speed value {} in JSON policy file, valid value should be [0, 100]'. + format(speed)) + self.speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_SPEED]) + else: + raise ValueError('SetFanSpeedAction missing mandatory field {} in JSON policy file'. + format(SetFanSpeedAction.JSON_FIELD_SPEED)) + + @classmethod + def set_all_fan_speed(cls, thermal_info_dict, speed): + from .thermal_infos import FanInfo + if FanInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[FanInfo.INFO_NAME], FanInfo): + fan_info_obj = thermal_info_dict[FanInfo.INFO_NAME] + for fan in fan_info_obj.get_all_fans(): + fan.set_speed(int(speed)) + + +@thermal_json_object('fan.all.set_speed') +class SetAllFanSpeedAction(SetFanSpeedAction): + """ + Action to set speed for all fans + """ + def execute(self, thermal_info_dict): + """ + Set speed for all fans + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + SetAllFanSpeedAction.set_all_fan_speed(thermal_info_dict, self.speed) + + +class LinearFanController(): + """ + Common Linear FAN Controller for B2F and F2B + """ + def __init__(self, low_temp, high_temp, hyst_temp, low_pwm, high_pwm): + self._low_temp = low_temp + self._high_temp = high_temp + self._hyst_temp = hyst_temp + self._low_pwm = low_pwm + self._high_pwm = high_pwm + self._linear_slope = (high_pwm - low_pwm) / (high_temp - low_temp) + self._last_pwm = None + + def calc_fan_speed(self, thermal_data): + temp = thermal_data.curr_temp + descend = thermal_data.temp_descend + + low_temp = self._low_temp - self._hyst_temp if descend else self._low_temp + high_temp = self._high_temp - self._hyst_temp if descend else self._high_temp + if temp <= low_temp: + sonic_logger.log_debug("[LinearController] temp: {} equal or lower than low temp: {}, set to lowest pwm: {}".format(temp, low_temp, self._low_pwm)) + self._last_pwm = self._low_pwm + return self._low_pwm + elif temp >= high_temp: + sonic_logger.log_debug("[LinearController] temp: {} equal or higher than high temp: {}, set to highest pwm: {}".format(temp, high_temp, self._high_pwm)) + self._last_pwm = self._high_pwm + return self._high_pwm + else: + pwm = float(self._linear_slope * (temp - low_temp) + self._low_pwm) + if descend: + if self._last_pwm != None and pwm > self._last_pwm: + pwm = self._last_pwm + else: + if self._last_pwm != None and pwm < self._last_pwm: + pwm = self._last_pwm + self._last_pwm = pwm + sonic_logger.log_debug("[LinearController] temp: {}, slope: {}, low_temp: {}, low_pwm: {}, set to pwm: {}".format(temp, self._linear_slope, low_temp, self._low_pwm, pwm)) + return pwm + +class PIDFanController(): + """ + Common FAN PID controller for CPU and BCM Temp + """ + MAX_SPEED = 255 + MIN_SPEED = 89 + def __init__(self, setpoint, p_val, i_val, d_val): + self._setpoint = setpoint + self._p = p_val + self._i = i_val + self._d = d_val + self._curr_speed = self.MIN_SPEED + + def calc_fan_speed(self, thermal_data): + hist2_temp = thermal_data.hist2_temp + hist1_temp = thermal_data.hist1_temp + temp = thermal_data.curr_temp + + if hist2_temp == None or hist1_temp == None: + return round(self._curr_speed / 2.55) + speed = self._curr_speed + self._p * (temp - hist1_temp) \ + + self._i * (temp - self._setpoint) + self._d * (temp - 2 * hist1_temp + hist2_temp) + if speed > self.MAX_SPEED: + speed = self.MAX_SPEED + elif speed < self.MIN_SPEED: + speed = self.MIN_SPEED + self._curr_speed = speed + speed_percent = float(speed / 2.55) + sonic_logger.log_debug("[PIDController] setpoint: {} p: {} i: {} d: {}, temp: {} hist_temp1: {} hist_temp2: {}, pwm: {}, percent: {}".format(self._setpoint, self._p, self._i, self._d, temp, hist1_temp, hist2_temp, speed, speed_percent)) + return speed_percent + + +@thermal_json_object('thermal.temp_check_and_fsc_algo_control') +class ThermalAlgorithmAction(SetFanSpeedAction): + """ + Action to check thermal sensor temperature change status and set speed for all fans + """ + THERMAL_LOG_LEVEL = "thermal_log_level" + CPU_PID_PARAMS = "cpu_pid_params" + BCM_PID_PARAMS = "bcm_pid_params" + F2B_LINEAR_PARAMS = "f2b_linear_params" + B2F_LINEAR_PARAMS = "b2f_linear_params" + + def __init__(self): + SetFanSpeedAction.__init__(self) + self.sys_airflow = None + self.cpu_pid_params = None + self.bcm_pid_params = None + self.f2b_linear_params = None + self.b2f_linear_params = None + self.cpu_fan_controller = None + self.bcm_fan_controller = None + self.linear_fan_controller = None + + def load_from_json(self, json_obj): + """ + Construct ThermalAlgorithmAction via JSON. JSON example: + { + "type": "thermal.temp_check_and_fsc_algo_control", + "cpu_pid_params": [82, 3, 0.5, 0.2], + "bcm_pid_params": [88, 4, 0.3, 0.4], + "f2b_linear_params": [34, 54, 3, 35, 100], + "b2f_linear_params": [28, 48, 3, 35, 100] + } + :param json_obj: A JSON object representing a ThermalAlgorithmAction action. + :return: + """ + if self.THERMAL_LOG_LEVEL in json_obj: + thermal_log_level = json_obj[self.THERMAL_LOG_LEVEL] + if not isinstance(thermal_log_level, int) or thermal_log_level not in range(0,8): + raise ValueError('ThermalAlgorithmAction invalid thermal log level, a interger in range 0-7 is required') + sonic_logger.set_min_log_priority(thermal_log_level) + if self.CPU_PID_PARAMS in json_obj: + cpu_pid_params = json_obj[self.CPU_PID_PARAMS] + if not isinstance(cpu_pid_params, list) or len(cpu_pid_params) != 4: + raise ValueError('ThermalAlgorithmAction invalid SetPoint PID {} in JSON policy file, valid value should be [point, p, i, d]'. + format(cpu_pid_params)) + self.cpu_pid_params = cpu_pid_params + else: + raise ValueError('ThermalAlgorithmAction missing mandatory field [setpoint, p, i, d] in JSON policy file') + + if self.BCM_PID_PARAMS in json_obj: + bcm_pid_params = json_obj[self.BCM_PID_PARAMS] + if not isinstance(bcm_pid_params, list) or len(bcm_pid_params) != 4: + raise ValueError('ThermalAlgorithmAction invalid SetPoint PID {} in JSON policy file, valid value should be [point, p, i, d]'. + format(bcm_pid_params)) + self.bcm_pid_params = bcm_pid_params + else: + raise ValueError('ThermalAlgorithmAction missing mandatory field [setpoint, p, i, d] in JSON policy file') + + if self.F2B_LINEAR_PARAMS in json_obj: + f2b_linear_params = json_obj[self.F2B_LINEAR_PARAMS] + if not isinstance(f2b_linear_params, list) or len(f2b_linear_params) != 5: + raise ValueError('ThermalAlgorithmAction invalid SetPoint PID {} in JSON policy file, valid value should be [point, p, i, d]'. + format(f2b_linear_params)) + self.f2b_linear_params = f2b_linear_params + else: + raise ValueError('ThermalAlgorithmAction missing mandatory field [low_temp, high_temp, hyst_temp, low_pwm, high_pwm] in JSON policy file') + + if self.B2F_LINEAR_PARAMS in json_obj: + b2f_linear_params = json_obj[self.B2F_LINEAR_PARAMS] + if not isinstance(b2f_linear_params, list) or len(b2f_linear_params) != 5: + raise ValueError('ThermalAlgorithmAction invalid SetPoint PID {} in JSON policy file, valid value should be [point, p, i, d]'. + format(b2f_linear_params)) + self.b2f_linear_params = b2f_linear_params + else: + raise ValueError('ThermalAlgorithmAction missing mandatory field [low_temp, high_temp, hyst_temp, low_pwm, high_pwm] in JSON policy file') + + sonic_logger.log_info("[ThermalAlgorithmAction] cpu_pid: {}, bcm_pid: {}, f2b_linear: {}, b2f_linear: {}".format(self.cpu_pid_params, self.bcm_pid_params, self.f2b_linear_params, self.b2f_linear_params)) + + def execute(self, thermal_info_dict): + """ + Check check thermal sensor temperature change status and set speed for all fans + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + if self.sys_airflow == None: + from .thermal_infos import ChassisInfo + if ChassisInfo.INFO_NAME in thermal_info_dict: + chassis_info_obj = thermal_info_dict[ChassisInfo.INFO_NAME] + chassis = chassis_info_obj.get_chassis() + self.sys_airflow = chassis.get_system_airflow() + + if self.cpu_fan_controller == None: + self.cpu_fan_controller = PIDFanController(self.cpu_pid_params[0], self.cpu_pid_params[1], + self.cpu_pid_params[2], self.cpu_pid_params[3]) + if self.bcm_fan_controller == None: + self.bcm_fan_controller = PIDFanController(self.bcm_pid_params[0], self.bcm_pid_params[1], + self.bcm_pid_params[2], self.bcm_pid_params[3]) + if self.linear_fan_controller == None: + if self.sys_airflow == 'INTAKE': + linear_params = self.b2f_linear_params + else: + linear_params = self.f2b_linear_params + self.linear_fan_controller = LinearFanController(linear_params[0], linear_params[1], linear_params[2], \ + linear_params[3], linear_params[4]) + + from .thermal_infos import ThermalInfo + if ThermalInfo.INFO_NAME in thermal_info_dict and \ + isinstance(thermal_info_dict[ThermalInfo.INFO_NAME], ThermalInfo): + + thermal_info_obj = thermal_info_dict[ThermalInfo.INFO_NAME] + thermals_data = thermal_info_obj.get_thermals_data() + cpu_thermal_data = thermals_data["CPU_Temp"] + cpu_fan_pwm = self.cpu_fan_controller.calc_fan_speed(cpu_thermal_data) + bcm_thermal_data = thermals_data["BCM_SW_Temp"] + bcm_fan_pwm = self.bcm_fan_controller.calc_fan_speed(bcm_thermal_data) + if self.sys_airflow == 'INTAKE': + thermal_data = thermals_data["Base_Temp_U5"] + linear_fan_pwm1 = self.linear_fan_controller.calc_fan_speed(thermal_data) + thermal_data = thermals_data["Base_Temp_U56"] + linear_fan_pwm2 = self.linear_fan_controller.calc_fan_speed(thermal_data) + else: + thermal_data = thermals_data["Switch_Temp_U28"] + linear_fan_pwm1 = self.linear_fan_controller.calc_fan_speed(thermal_data) + thermal_data = thermals_data["Switch_Temp_U29"] + linear_fan_pwm2 = self.linear_fan_controller.calc_fan_speed(thermal_data) + target_fan_pwm = max(cpu_fan_pwm, bcm_fan_pwm, linear_fan_pwm1, linear_fan_pwm2) + sonic_logger.log_info("[ThermalAlgorithmAction] cpu_pid_pwm: {}, bcm_pid_pwm: {}, linear_fan_pwm: {}, linear_fan_pwm2: {}, target_pwm: {}".format(cpu_fan_pwm, bcm_fan_pwm, linear_fan_pwm1, linear_fan_pwm2, target_fan_pwm)) + SetAllFanSpeedAction.set_all_fan_speed(thermal_info_dict, round(target_fan_pwm)) + + +@thermal_json_object('switch.shutdown') +class SwitchPolicyAction(ThermalPolicyActionBase): + """ + Base class for thermal action. Once all thermal conditions in a thermal policy are matched, + all predefined thermal action will be executed. + """ + def execute(self, thermal_info_dict): + """ + Take action when thermal condition matches. For example, adjust speed of fan or shut + down the switch. + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + sonic_logger.log_warning("Alarm for temperature critical is detected, shutdown Device") + # Wait for 30s then shutdown + import time + time.sleep(30) + # Power off COMe through CPLD + CPLD_POWRE_OFF_CMD = "echo 0xa120 0xfc > /sys/bus/platform/devices/baseboard/setreg" + api_helper = APIHelper() + api_helper.get_cmd_output(CPLD_POWER_OFF_CMD) + + +@thermal_json_object('thermal_control.control') +class ControlThermalAlgoAction(ThermalPolicyActionBase): + """ + Action to control the thermal control algorithm + """ + # JSON field definition + JSON_FIELD_STATUS = 'status' + + def __init__(self): + self.status = True + + def load_from_json(self, json_obj): + """ + Construct ControlThermalAlgoAction via JSON. JSON example: + { + "type": "thermal_control.control" + "status": "true" + } + :param json_obj: A JSON object representing a ControlThermalAlgoAction action. + :return: + """ + if ControlThermalAlgoAction.JSON_FIELD_STATUS in json_obj: + status_str = json_obj[ControlThermalAlgoAction.JSON_FIELD_STATUS].lower() + if status_str == 'true': + self.status = True + elif status_str == 'false': + self.status = False + else: + raise ValueError('Invalid {} field value, please specify true of false'. + format(ControlThermalAlgoAction.JSON_FIELD_STATUS)) + else: + raise ValueError('ControlThermalAlgoAction ' + 'missing mandatory field {} in JSON policy file'. + format(ControlThermalAlgoAction.JSON_FIELD_STATUS)) + + def execute(self, thermal_info_dict): + """ + Disable thermal control algorithm + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + from .thermal_infos import ChassisInfo + if ChassisInfo.INFO_NAME in thermal_info_dict: + chassis_info_obj = thermal_info_dict[ChassisInfo.INFO_NAME] + chassis = chassis_info_obj.get_chassis() + thermal_manager = chassis.get_thermal_manager() + if self.status: + thermal_manager.start_thermal_control_algorithm() + else: + thermal_manager.stop_thermal_control_algorithm() diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_conditions.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_conditions.py new file mode 100644 index 000000000000..c8bb51ef966c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_conditions.py @@ -0,0 +1,121 @@ +from sonic_platform_base.sonic_thermal_control.thermal_condition_base import ThermalPolicyConditionBase +from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object + + +class FanCondition(ThermalPolicyConditionBase): + def get_fan_info(self, thermal_info_dict): + from .thermal_infos import FanInfo + if FanInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[FanInfo.INFO_NAME], FanInfo): + return thermal_info_dict[FanInfo.INFO_NAME] + else: + return None + +@thermal_json_object('fantray.any.absence') +class AnyFantrayAbsenceCondition(FanCondition): + def is_match(self, thermal_info_dict): + fan_info_obj = self.get_fan_info(thermal_info_dict) + return len(fan_info_obj.get_absence_fantrays()) > 0 if fan_info_obj else False + +@thermal_json_object('fantray.all.absence') +class AllFantrayAbsenceCondition(FanCondition): + def is_match(self, thermal_info_dict): + fan_info_obj = self.get_fan_info(thermal_info_dict) + return len(fan_info_obj.get_presence_fantrays()) == 0 if fan_info_obj else False + +@thermal_json_object('fantray.all.presence') +class AllFantrayPresenceCondition(FanCondition): + def is_match(self, thermal_info_dict): + fan_info_obj = self.get_fan_info(thermal_info_dict) + return len(fan_info_obj.get_absence_fantrays()) == 0 if fan_info_obj else True + +@thermal_json_object('fan.rotor.more_than_one.failed') +class FanRotorMoreThanOneFailedCondition(FanCondition): + def is_match(self, thermal_info_dict): + fan_info_obj = self.get_fan_info(thermal_info_dict) + return len(fan_info_obj.get_absence_fans()) > 1 if fan_info_obj else False + +@thermal_json_object('fan.rotor.less_than_two.failed') +class FanRotorLessThanTwoFailedCondition(FanCondition): + def is_match(self, thermal_info_dict): + fan_info_obj = self.get_fan_info(thermal_info_dict) + return len(fan_info_obj.get_absence_fans()) < 2 if fan_info_obj else True + +class PsuCondition(ThermalPolicyConditionBase): + def get_psu_info(self, thermal_info_dict): + from .thermal_infos import PsuInfo + if PsuInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[PsuInfo.INFO_NAME], PsuInfo): + return thermal_info_dict[PsuInfo.INFO_NAME] + else: + return None + +@thermal_json_object('psu.any.absence') +class AnyPsuAbsenceCondition(PsuCondition): + def is_match(self, thermal_info_dict): + psu_info_obj = self.get_psu_info(thermal_info_dict) + return len(psu_info_obj.get_absence_psus()) > 0 if psu_info_obj else False + +@thermal_json_object('psu.all.absence') +class AllPsuAbsenceCondition(PsuCondition): + def is_match(self, thermal_info_dict): + psu_info_obj = self.get_psu_info(thermal_info_dict) + return len(psu_info_obj.get_presence_psus()) == 0 if psu_info_obj else False + +@thermal_json_object('psu.all.presence') +class AllPsuPresenceCondition(PsuCondition): + def is_match(self, thermal_info_dict): + psu_info_obj = self.get_psu_info(thermal_info_dict) + return len(psu_info_obj.get_absence_psus()) == 0 if psu_info_obj else True + + +class ThermalCondition(ThermalPolicyConditionBase): + def get_thermal_info(self, thermal_info_dict): + from .thermal_infos import ThermalInfo + if ThermalInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[ThermalInfo.INFO_NAME], ThermalInfo): + return thermal_info_dict[ThermalInfo.INFO_NAME] + else: + return None + +@thermal_json_object('thermal.over.high_critical_threshold') +class ThermalOverHighCriticalCondition(ThermalCondition): + def is_match(self, thermal_info_dict): + thermal_info_obj = self.get_thermal_info(thermal_info_dict) + if thermal_info_obj: + return thermal_info_obj.is_any_over_high_critical_threshold() + else: + return False + +@thermal_json_object('thermal.any.over.high_threshold') +class AnyThermalOverHighThresholdCondition(ThermalCondition): + def is_match(self, thermal_info_dict): + thermal_info_obj = self.get_thermal_info(thermal_info_dict) + if thermal_info_obj: + return thermal_info_obj.is_any_warm_up_and_over_high_threshold() + else: + return False + +@thermal_json_object('thermal.any.below.low_threshold') +class AnyThermalBelowLowThresholdCondition(ThermalCondition): + def is_match(self, thermal_info_dict): + thermal_info_obj = self.get_thermal_info(thermal_info_dict) + if thermal_info_obj: + return thermal_info_obj.is_any_cool_down_and_below_low_threshold() + else: + return False + +@thermal_json_object('thermal.all.below.high_threshold') +class AnyThermalOverHighThresholdCondition(ThermalCondition): + def is_match(self, thermal_info_dict): + thermal_info_obj = self.get_thermal_info(thermal_info_dict) + if thermal_info_obj: + return not thermal_info_obj.is_any_warm_up_and_over_high_threshold() + else: + return True + +@thermal_json_object('thermal.all.over.low_threshold') +class AnyThermalBelowLowThresholdCondition(ThermalCondition): + def is_match(self, thermal_info_dict): + thermal_info_obj = self.get_thermal_info(thermal_info_dict) + if thermal_info_obj: + return not thermal_info_obj.is_any_cool_down_and_below_low_threshold() + else: + return True diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_infos.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_infos.py new file mode 100644 index 000000000000..4925637f8cb3 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_infos.py @@ -0,0 +1,362 @@ +from sonic_platform_base.sonic_thermal_control.thermal_info_base import ThermalPolicyInfoBase +from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object +from sonic_py_common import logger + +sonic_logger = logger.Logger('thermal_infos') + +@thermal_json_object('fan_info') +class FanInfo(ThermalPolicyInfoBase): + """ + Fan information needed by thermal policy + """ + + # Fan information name + INFO_NAME = 'fan_info' + FAN_LOW_WARNING_SPEED = 1370 + FAN_FRONT_HIGH_WARNING_SPEED = 29125 + FAN_REAR_HIGH_WARNING_SPEED = 25375 + + def __init__(self): + self._all_fans = set() + self._absence_fans = set() + self._presence_fans = set() + self._absence_fantrays = set() + self._presence_fantrays = set() + self._low_warning_fans = set() + self._high_warning_fans = set() + self._status_changed = False + + def collect(self, chassis): + """ + Collect absence and presence fans. + :param chassis: The chassis object + :return: + """ + self._status_changed = False + try: + for fantray in chassis.get_all_fan_drawers(): + if fantray.get_presence() and fantray not in self._presence_fantrays: + self._presence_fantrays.add(fantray) + self._status_changed = True + if fantray in self._absence_fantrays: + self._absence_fantrays.remove(fantray) + elif not fantray.get_presence() and fantray not in self._absence_fantrays: + self._absence_fantrays.add(fantray) + self._status_changed = True + if fantray in self._presence_fantrays: + self._presence_fantrays.remove(fantray) + + for fan in fantray.get_all_fans(): + if fan.get_presence() and fantray not in self._presence_fans: + self._presence_fans.add(fan) + self._status_changed = True + if fan in self._absence_fans: + self._absence_fans.remove(fan) + elif not fan.get_presence() and fan not in self._absence_fans: + self._absence_fans.add(fan) + self._status_changed = True + if fan in self._presence_fans: + self._presence_fans.remove(fan) + + fan_name = fan.get_name() + fan_rpm = fan.get_speed_rpm() + if fan not in self._all_fans: + self._all_fans.add(fan) + # FAN Low speed warning + if fan_rpm < self.FAN_LOW_WARNING_SPEED and fan not in self._low_warning_fans: + self._low_warning_fans.add(fan) + sonic_logger.log_warning("FAN {} speed {}, low speed warning".format(fan_name, fan_rpm)) + elif fan_rpm > self.FAN_LOW_WARNING_SPEED and fan in self._low_warning_fans: + sonic_logger.log_notice("FAN {}, restore from low speed warning".format(fan_name)) + self._low_warning_fans.remove(fan) + # FAN high speed warning + if fan.fan_index == 1: + if fan_rpm > self.FAN_FRONT_HIGH_WARNING_SPEED and fan not in self._high_warning_fans: + self._high_warning_fans.add(fan) + sonic_logger.log_warning("FAN {} speed {}, high speed warning".format(fan_name, fan_rpm)) + elif fan_rpm > self.FAN_FRONT_HIGH_WARNING_SPEED and fan in self._high_warning_fans: + self._high_warning_fans.remove(fan) + sonic_logger.log_notice("FAN {}, restore from high speed warning".format(fan_name)) + else: + if fan_rpm > self.FAN_REAR_HIGH_WARNING_SPEED and fan not in self._high_warning_fans: + self._high_warning_fans.add(fan) + sonic_logger.log_warning("FAN {} speed {}, high speed warning".format(fan_name, fan_rpm)) + elif fan_rpm > self.FAN_REAR_HIGH_WARNING_SPEED and fan in self._high_warning_fans: + self._high_warning_fans.remove(fan) + sonic_logger.log_notice("FAN {}, restore from high speed warning".format(fan_name)) + except Exception as e: + sonic_logger.log_warning("Catch exception: {}, File: {}, Line: {}".format(type(e).__name__, __file__, e.__traceback__.tb_lineno)) + + def get_all_fans(self): + """ + Retrieves all fans + :return: A set of fans + """ + return self._all_fans + + def get_absence_fantrays(self): + """ + Retrieves absence fans + :return: A set of absence fantrays + """ + return self._absence_fantrays + + def get_presence_fantrays(self): + """ + Retrieves presence fans + :return: A set of presence fantrays + """ + return self._presence_fantrays + + def get_absence_fans(self): + """ + Retrieves absence fans + :return: A set of absence fans + """ + return self._absence_fans + + def get_presence_fans(self): + """ + Retrieves presence fans + :return: A set of presence fans + """ + return self._presence_fans + + def get_low_warning_fans(self): + """ + Retrieves low rpm warning fans + :return: A set of low rpm warning fans + """ + return self._low_warning_fans + + def get_high_warning_fans(self): + """ + Retrieves high rpm warning fans + :return: A set of high rpm warning fans + """ + return self._high_warning_fans + + def is_status_changed(self): + """ + Retrieves if the status of fan information changed + :return: True if status changed else False + """ + return self._status_changed + + +@thermal_json_object('psu_info') +class PsuInfo(ThermalPolicyInfoBase): + """ + PSU information needed by thermal policy + """ + INFO_NAME = 'psu_info' + PSU_TEMP_HIGH_WARNING_THRESHOLD = 65 + + def __init__(self): + self._absence_psus = set() + self._presence_psus = set() + self._high_warning_psus = set() + self._status_changed = False + + def collect(self, chassis): + """ + Collect absence and presence PSUs. + :param chassis: The chassis object + :return: + """ + self._status_changed = False + try: + for psu in chassis.get_all_psus(): + if psu.get_presence() and psu not in self._presence_psus: + self._presence_psus.add(psu) + self._status_changed = True + if psu in self._absence_psus: + self._absence_psus.remove(psu) + elif (not psu.get_presence()) and psu not in self._absence_psus: + self._absence_psus.add(psu) + self._status_changed = True + if psu in self._presence_psus: + self._presence_psus.remove(psu) + # PSU Temp high warning + psu_name = psu.get_name() + psu_temp = psu.get_temperature() + if psu_temp != None and psu_temp != 'N/A': + if psu_temp > self.PSU_TEMP_HIGH_WARNING_THRESHOLD and psu not in self._high_warning_psus: + self._high_warning_psus.add(psu) + sonic_logger.log_warning("PSU {} temp {}, high temperature warning".format(psu_name, psu_temp)) + elif psu_temp < self.PSU_TEMP_HIGH_WARNING_THRESHOLD and psu in self._high_warning_psus: + self._high_warning_psus.remove(psu) + sonic_logger.log_notice("PSU {} restore from high temperature warning".format(psu_name)) + except Exception as e: + sonic_logger.log_warning("Catch exception: {}, File: {}, Line: {}".format(type(e).__name__, __file__, e.__traceback__.tb_lineno)) + + def get_absence_psus(self): + """ + Retrieves presence PSUs + :return: A set of absence PSUs + """ + return self._absence_psus + + def get_presence_psus(self): + """ + Retrieves presence PSUs + :return: A set of presence fans + """ + return self._presence_psus + + def get_high_warning_psus(self): + """ + Retrieves high temperature warning PSUs + :return: A set of high temp fans + """ + return self._high_warning_psus + + def is_status_changed(self): + """ + Retrieves if the status of PSU information changed + :return: True if status changed else False + """ + return self._status_changed + + +class ThermalData(): + def __init__(self, name): + self.name = name + self.hist2_temp = None + self.hist1_temp = None + self.curr_temp = None + self.temp_descend = False + + def update_temp(self, temp): + self.hist2_temp = self.hist1_temp + self.hist1_temp = self.curr_temp + self.curr_temp = temp + return self + + def update_temp_trend(self): + if self.hist1_temp == None: + self.temp_descend = False + elif self.curr_temp < self.hist1_temp: + self.temp_descend = True + else: + self.temp_descend = False + return self + +@thermal_json_object('thermal_info') +class ThermalInfo(ThermalPolicyInfoBase): + """ + Thermal information needed by thermal policy + """ + + # Fan information name + INFO_NAME = 'thermal_info' + THERMAL_SHUTDOWN_TEMP_MAP = {"CPU_Temp": 93, "BCM_SW_Temp": 120} + + def __init__(self): + self.init = False + self._high_warning_thermals = set() + self._low_warning_thermals = set() + self._high_shutdown_thermals = set() + self._thermals_data = {} + + def collect(self, chassis): + """ + Collect thermal sensor temperature change status + :param chassis: The chassis object + :return: + """ + try: + for thermal in chassis.get_all_thermals(): + name = thermal.get_name() + temp = thermal.get_temperature() + if temp == None or temp == 'N/A': + continue + high_threshold = thermal.get_high_threshold() + low_threshold = thermal.get_low_threshold() + thermal_shutdown = self.THERMAL_SHUTDOWN_TEMP_MAP.get(name, None) + + # Collect thermal data + thermal_data = self._thermals_data.get(name, None) + if thermal_data == None: + thermal_data = ThermalData(name) + self._thermals_data[name] = thermal_data + thermal_data.update_temp(temp).update_temp_trend() + + # Handle high threshold condition + if high_threshold != None and high_threshold != 'N/A': + if temp > high_threshold and thermal not in self._high_warning_thermals: + self._high_warning_thermals.add(thermal) + sonic_logger.log_warning("Thermal {} temp {}, high threshold warning".format(name, temp)) + if thermal_shutdown != None and temp > thermal_shutdown: + self._high_shutdown_thermals.add(thermal) + sonic_logger.log_warning("Thermal {} temp {}, high temp shutdown warning".format(name, temp)) + elif temp < (high_threshold - 3) and thermal in self._high_warning_thermals: + self._high_warning_thermals.remove(thermal) + sonic_logger.log_notice("Thermal {}, restore from high threshold warning".format(name)) + if thermal in self._high_shutdown_thermals: + self._high_shutdown_thermals.remove(thermal) + + # Handle low threshold condition + if low_threshold != None and low_threshold != 'N/A': + if temp < low_threshold and thermal not in self._low_warning_thermals: + self._low_warning_thermals.add(thermal) + sonic_logger.log_warning("Thermal {} temp {}, low threshold warning".format(name, temp)) + elif temp > (low_threshold + 3) and thermal in self._low_warning_thermals: + self._low_warning_thermals.remove(thermal) + sonic_logger.log_notice("Thermal {}, restore from low threshold warning".format(name)) + except Exception as e: + sonic_logger.log_warning("Catch exception: {}, File: {}, Line: {}".format(type(e).__name__, __file__, e.__traceback__.tb_lineno)) + + def is_any_warm_up_and_over_high_threshold(self): + """ + Retrieves if the temperature is warm up and over high threshold + :return: True if the temperature is warm up and over high threshold else False + """ + return len(self._high_warning_thermals) > 0 + + def is_any_cool_down_and_below_low_threshold(self): + """ + Retrieves if the temperature is cold down and below low threshold + :return: True if the temperature is cold down and below low threshold else False + """ + return len(self._low_warning_thermals) > 0 + + def is_any_over_high_critical_threshold(self): + """ + Retrieves if the temperature is over high critical threshold + :return: True if the temperature is over high critical threshold else False + """ + return len(self._high_shutdown_thermals) > 0 + + def get_thermals_data(self): + """ + Retrieves all the thermal data + :return: thermal data dict using thermal name as key + """ + return self._thermals_data + + +@thermal_json_object('chassis_info') +class ChassisInfo(ThermalPolicyInfoBase): + """ + Chassis information needed by thermal policy + """ + INFO_NAME = 'chassis_info' + + def __init__(self): + self._chassis = None + + def collect(self, chassis): + """ + Collect platform chassis. + :param chassis: The chassis object + :return: + """ + self._chassis = chassis + + def get_chassis(self): + """ + Retrieves platform chassis object + :return: A platform chassis object. + """ + return self._chassis diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_manager.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_manager.py new file mode 100644 index 000000000000..fb9557270f05 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/thermal_manager.py @@ -0,0 +1,21 @@ +from sonic_platform_base.sonic_thermal_control.thermal_manager_base import ThermalManagerBase +from .thermal_infos import * +from .thermal_conditions import * +from .thermal_actions import * + +class ThermalManager(ThermalManagerBase): + @classmethod + def initialize(cls): + """ + Initialize thermal manager, including register thermal condition types and thermal action types + and any other vendor specific initialization. + """ + return True + + @classmethod + def deinitialize(cls): + """ + Destroy thermal manager, including any vendor specific cleanup. + :return: + """ + return True diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/watchdog.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/watchdog.py new file mode 100644 index 000000000000..90b5ee352c08 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/watchdog.py @@ -0,0 +1,14 @@ +try: + from sonic_platform_pddf_base.pddf_watchdog import PddfWatchdog +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + + +class Watchdog(PddfWatchdog): + """PDDF Platform-Specific Watchdog Class""" + + def __init__(self): + PddfWatchdog.__init__(self) + + # Provide the functions/variables below for which implementation is to be overwritten diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/scripts/pddf_post_device_create.sh b/platform/broadcom/sonic-platform-modules-cel/ds3000/scripts/pddf_post_device_create.sh new file mode 100755 index 000000000000..c2f193d1bc20 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/scripts/pddf_post_device_create.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +declare -r CPLD_SETREG="/sys/bus/platform/devices/baseboard/setreg" +declare -r CPLD_GETREG="/sys/bus/platform/devices/baseboard/getreg" + +# Load fpga extend driver after fpga device created +modprobe pddf_custom_fpga_extend + +# attach ucd90120 devices, bus 107, devices 0x34 and 0x35 +echo "ucd90120 0x34" > /sys/bus/i2c/devices/i2c-107/new_device +echo "ucd90120 0x35" > /sys/bus/i2c/devices/i2c-107/new_device + +# attach mp2975 devices, bus 108, devices 0x70 and 0x7a +echo "mp2975 0x70" > /sys/bus/i2c/devices/i2c-108/new_device +echo "mp2975 0x7a" > /sys/bus/i2c/devices/i2c-108/new_device + +# Set SYS_LED to Green, assuming everything came up fine. +echo "0xa162 0xdc" > ${CPLD_SETREG} + +# Disable CPLD thermal shutdown by default +echo "0xa175 0x0" > ${CPLD_SETREG} diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/scripts/pddf_pre_driver_install.sh b/platform/broadcom/sonic-platform-modules-cel/ds3000/scripts/pddf_pre_driver_install.sh new file mode 100755 index 000000000000..29bc603d447c --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/scripts/pddf_pre_driver_install.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Has customized those drivers,so rename them to lose effect +psu_driver=pddf_psu_driver_module.ko +ker_name=$(uname -r) +driver_path=/usr/lib/modules/${ker_name}/extra/ +if [ -e ${driver_path}${psu_driver} ]; then + mv ${driver_path}${psu_driver} ${driver_path}${psu_driver}-bk +fi + +echo 'pddf psu driver module has rename now' diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/scripts/pre_pddf_init.sh b/platform/broadcom/sonic-platform-modules-cel/ds3000/scripts/pre_pddf_init.sh new file mode 100755 index 000000000000..2556858cdab9 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/scripts/pre_pddf_init.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Probe Baseboard CPLD driver +modprobe baseboard_cpld +sleep 1 + +# Get BMC mode +PLATFORM=`sed -n 's/onie_platform=\(.*\)/\1/p' /host/machine.conf` +BMC_PRESENCE_SYS_PATH="/sys/bus/platform/devices/baseboard/bmc_presence" +BMC_PRESENCE=`cat ${BMC_PRESENCE_SYS_PATH}` +echo "Platform ${PLATFORM} BMC card ${BMC_PRESENCE}" + +# Copy pddf-device.json according to bmc mode +PDDF_JSON="pddf-device.json" +PDDF_JSON_BMC="pddf-device-bmc.json" +PDDF_JSON_NONBMC="pddf-device-nonbmc.json" +PDDF_JSON_PATH="/usr/share/sonic/device/${PLATFORM}/pddf" +PLATFORM_COMPONENTS_FILE="/usr/share/sonic/device/${PLATFORM}/platform_components.json" +if [ ${BMC_PRESENCE} == "present" ]; then + cp ${PDDF_JSON_PATH}/${PDDF_JSON_BMC} ${PDDF_JSON_PATH}/${PDDF_JSON} + # Add BMC component if BMC exists + if ! grep -q "BMC" ${PLATFORM_COMPONENTS_FILE}; then + sed -i '6i \ "BMC": {},' ${PLATFORM_COMPONENTS_FILE} + fi +else + # BMC Card absent + cp ${PDDF_JSON_PATH}/${PDDF_JSON_NONBMC} ${PDDF_JSON_PATH}/${PDDF_JSON} + # Remove BMC component for NonBMC + sed -i '/"BMC"/d' ${PLATFORM_COMPONENTS_FILE} +fi diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/systemd/pddf-platform-init.service b/platform/broadcom/sonic-platform-modules-cel/ds3000/systemd/pddf-platform-init.service new file mode 100644 index 000000000000..f4098b34b60e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/systemd/pddf-platform-init.service @@ -0,0 +1,16 @@ +[Unit] +Description=PDDF module and device initialization service +Before=pmon.service watchdog-control.service +Before=opennsl-modules.service +DefaultDependencies=no + +[Service] +Type=oneshot +ExecStartPre=-/usr/local/bin/pre_pddf_init.sh +ExecStart=/usr/local/bin/pddf_util.py install +ExecStop=/usr/local/bin/pddf_util.py clean +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +WantedBy=opennsl-modules.service diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/utils/afulnx_64 b/platform/broadcom/sonic-platform-modules-cel/ds3000/utils/afulnx_64 new file mode 100755 index 0000000000000000000000000000000000000000..0ba1549c2891bac2e3c6aa385c5c2182c5319826 GIT binary patch literal 1134256 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

N-hG0B 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�&@`Zc)D`wR2w+3`ZMQIZA@(_X}5H<@$( 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 blLj{% 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 literal 0 HcmV?d00001 diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/utils/fpga_prog b/platform/broadcom/sonic-platform-modules-cel/ds3000/utils/fpga_prog new file mode 100755 index 0000000000000000000000000000000000000000..3227da590a0a516dadac3e4b7b0f5ce6a4f1fb66 GIT binary patch literal 17536 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 b)x>wONXX@~?nPznSKMZ4oM%Nvi|zjxa%%%M literal 0 HcmV?d00001 diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/utils/ispvm b/platform/broadcom/sonic-platform-modules-cel/ds3000/utils/ispvm new file mode 100755 index 0000000000000000000000000000000000000000..f8e216556ebe1f37526e30257aa4c6e278f8ba6b GIT binary patch literal 101752 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 bq+j^*hhILW>umj>pchAQwfH*ts@DGlP$Tz1 literal 0 HcmV?d00001 diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/utils/pddf_switch_svc.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/utils/pddf_switch_svc.py new file mode 100644 index 000000000000..319178d62a41 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/utils/pddf_switch_svc.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# Script to stop and start the respective platforms default services. +# This will be used while switching the pddf->non-pddf mode and vice versa +import os +import sys +import commands + +def check_pddf_support(): + return True + +def stop_platform_svc(): + status, output = commands.getstatusoutput("systemctl stop xxxx-platform-init.service") + if status: + print "Stop xxxx-platform-init.service failed %d"%status + return False + status, output = commands.getstatusoutput("systemctl disable xxxx-platform-init.service") + if status: + print "Disable xxxx-platform-init.service failed %d"%status + return False + + status, output = commands.getstatusoutput("/usr/local/bin/xxxx_util.py clean") + if status: + print "xxxx_util.py clean command failed %d"%status + return False + + # HACK , stop the pddf-platform-init service if it is active + status, output = commands.getstatusoutput("systemctl stop pddf-platform-init.service") + if status: + print "Stop pddf-platform-init.service along with other platform serives failed %d"%status + return False + + return True + +def start_platform_svc(): + status, output = commands.getstatusoutput("/usr/local/bin/xxxx_util.py install") + if status: + print "xxxx_util.py install command failed %d"%status + return False + + status, output = commands.getstatusoutput("systemctl enable xxxx-platform-init.service") + if status: + print "Enable xxxx-platform-init.service failed %d"%status + return False + status, output = commands.getstatusoutput("systemctl start xxxx-platform-init.service") + if status: + print "Start xxxx-platform-init.service failed %d"%status + return False + + return True + +def start_platform_pddf(): + # Enable PDDF 2.0 object class for xxxx + status, output = commands.getstatusoutput("mkdir /usr/share/sonic/platform/sonic_platform") + if status: + print "Unable to create 2.0 object class folder /usr/share/sonic/platform/sonic_platform" + return False + + status, output = commands.getstatusoutput("systemctl start pddf-platform-init.service") + if status: + print "Start pddf-platform-init.service failed %d"%status + return False + + return True + +def stop_platform_pddf(): + # Disable PDDF 2.0 object class for xxxx + status, output = commands.getstatusoutput("rm -r /usr/share/sonic/platform/sonic_platform") + if status: + print "Unable to delete 2.0 object class folder /usr/share/sonic/platform/sonic_platform" + return False + + status, output = commands.getstatusoutput("systemctl stop pddf-platform-init.service") + if status: + print "Stop pddf-platform-init.service failed %d"%status + return False + + return True + +def main(): + pass + +if __name__ == "__main__": + main() From 4929d34984979aa1aaf2298c68d7b814cb845d23 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sun, 7 Apr 2024 21:25:02 -0500 Subject: [PATCH 2/3] CEL_DS3000 add cel ds3000 platform config into common platform code --- platform/broadcom/one-image.mk | 1 + platform/broadcom/platform-modules-cel.mk | 6 ++++++ .../sonic-platform-modules-cel/debian/control | 5 +++++ .../sonic-platform-modules-cel/debian/rules | 18 ++++++++---------- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/platform/broadcom/one-image.mk b/platform/broadcom/one-image.mk index c9660299ed7e..150dfc7c2838 100755 --- a/platform/broadcom/one-image.mk +++ b/platform/broadcom/one-image.mk @@ -55,6 +55,7 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \ $(CEL_HALIBURTON_PLATFORM_MODULE) \ $(CEL_SEASTONE2_PLATFORM_MODULE) \ $(CEL_BELGITE_PLATFORM_MODULE) \ + $(CEL_DS3000_PLATFORM_MODULE) \ $(DELTA_AG9032V1_PLATFORM_MODULE) \ $(DELTA_AG9064_PLATFORM_MODULE) \ $(DELTA_AG5648_PLATFORM_MODULE) \ diff --git a/platform/broadcom/platform-modules-cel.mk b/platform/broadcom/platform-modules-cel.mk index b25aeb7c0900..61c015914ff2 100644 --- a/platform/broadcom/platform-modules-cel.mk +++ b/platform/broadcom/platform-modules-cel.mk @@ -5,12 +5,14 @@ CEL_HALIBURTON_PLATFORM_MODULE_VERSION = 0.9 CEL_SEASTONE2_PLATFORM_MODULE_VERSION = 0.9 CEL_SILVERSTONE_PLATFORM_MODULE_VERSION = 0.9 CEL_BELGITE_PLATFORM_MODULE_VERSION = 0.9 +CEL_DS3000_PLATFORM_MODULE_VERSION = 0.9 export CEL_DX010_PLATFORM_MODULE_VERSION export CEL_HALIBURTON_PLATFORM_MODULE_VERSION export CEL_SEASTONE2_PLATFORM_MODULE_VERSION export CEL_SILVERSTONE_PLATFORM_MODULE_VERSION export CEL_BELGITE_PLATFORM_MODULE_VERSION +export CEL_DS3000_PLATFORM_MODULE_VERSION CEL_DX010_PLATFORM_MODULE = platform-modules-dx010_$(CEL_DX010_PLATFORM_MODULE_VERSION)_amd64.deb $(CEL_DX010_PLATFORM_MODULE)_SRC_PATH = $(PLATFORM_PATH)/sonic-platform-modules-cel @@ -33,3 +35,7 @@ $(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_SILVERSTONE_P CEL_BELGITE_PLATFORM_MODULE = platform-modules-belgite_$(CEL_BELGITE_PLATFORM_MODULE_VERSION)_amd64.deb $(CEL_BELGITE_PLATFORM_MODULE)_PLATFORM = x86_64-cel_belgite-r0 $(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_BELGITE_PLATFORM_MODULE))) + +CEL_DS3000_PLATFORM_MODULE = platform-modules-ds3000_$(CEL_DS3000_PLATFORM_MODULE_VERSION)_amd64.deb +$(CEL_DS3000_PLATFORM_MODULE)_PLATFORM = x86_64-cel_ds3000-r0 +$(eval $(call add_extra_package,$(CEL_DX010_PLATFORM_MODULE),$(CEL_DS3000_PLATFORM_MODULE))) diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/control b/platform/broadcom/sonic-platform-modules-cel/debian/control index 0dd6eb76e00e..d9eced051abb 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/control +++ b/platform/broadcom/sonic-platform-modules-cel/debian/control @@ -30,3 +30,8 @@ Package: platform-modules-belgite Architecture: amd64 Depends: linux-image-6.1.0-11-2-amd64-unsigned Description: kernel modules for platform devices such as led, sfp + +Package: platform-modules-ds3000 +Architecture: amd64 +Depends: linux-image-6.1.0-11-2-amd64-unsigned +Description: kernel modules for platform devices such as led, sfp diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/rules b/platform/broadcom/sonic-platform-modules-cel/debian/rules index 7c81e2496f1d..21b8b1880795 100755 --- a/platform/broadcom/sonic-platform-modules-cel/debian/rules +++ b/platform/broadcom/sonic-platform-modules-cel/debian/rules @@ -6,7 +6,7 @@ export KBUILD_EXTRA_SYMBOLS := /sonic/platform/pddf/i2c/Module.symvers.PDDF KVERSION ?= $(shell uname -r) KERNEL_SRC := /lib/modules/$(KVERSION) MOD_SRC_DIR:= $(shell pwd) -MODULE_DIRS:= dx010 haliburton silverstone seastone2 belgite +MODULE_DIRS:= dx010 haliburton silverstone seastone2 belgite ds3000 %: dh $@ @@ -19,15 +19,13 @@ override_dh_auto_build: python3 -m build --wheel --no-isolation --outdir $(MOD_SRC_DIR)/$${mod}/modules; \ continue; \ fi; \ - if [ $$mod = "belgite" ]; then \ - cd $(MOD_SRC_DIR); \ - if [ -d $(MOD_SRC_DIR)/$${mod}/pddf ]; then \ - cd $(MOD_SRC_DIR)/$${mod}/pddf; \ - python3 -m build --wheel --no-isolation --outdir $(MOD_SRC_DIR)/$${mod}/pddf; \ - echo "Finished making pddf whl package for $$mod"; \ - fi; \ - continue; \ - fi; \ + cd $(MOD_SRC_DIR); \ + if [ -d $(MOD_SRC_DIR)/$${mod}/pddf ]; then \ + cd $(MOD_SRC_DIR)/$${mod}/pddf; \ + python3 -m build --wheel --no-isolation --outdir $(MOD_SRC_DIR)/$${mod}/pddf; \ + echo "Finished making pddf whl package for $$mod"; \ + continue; \ + fi; \ cd $(MOD_SRC_DIR)/$${mod}; \ python3 -m build --wheel --no-isolation --outdir $(MOD_SRC_DIR)/$${mod}/modules; \ done) From 4fce766a38e8148c68dc97aad10de79d689fd283 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Wed, 17 Apr 2024 21:20:15 -0500 Subject: [PATCH 3/3] Semgrep fix or bypass for ds3000 platform code --- .../ds3000/modules/baseboard_cpld.c | 2 +- .../modules/led_driver/pddf_custom_led_module.c | 8 ++++---- .../ds3000/modules/pddf_custom_fpga_extend.c | 2 +- .../ds3000/modules/psu_driver/pddf_psu_api.c | 16 ++++++++-------- .../ds3000/modules/psu_driver/pddf_psu_driver.c | 8 ++++---- .../ds3000/modules/switchboard_fpga.c | 8 ++++---- .../pddf/sonic_platform/custom_component.py | 4 ++-- .../ds3000/pddf/sonic_platform/fan.py | 6 +++--- .../ds3000/pddf/sonic_platform/helper.py | 4 ++-- 9 files changed, 29 insertions(+), 29 deletions(-) diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/baseboard_cpld.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/baseboard_cpld.c index dd5a8c4d267e..6107d9635438 100644 --- a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/baseboard_cpld.c +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/baseboard_cpld.c @@ -142,7 +142,7 @@ static ssize_t setreg_store(struct device *dev, struct device_attribute *devattr char *pclone = clone; char *last; - strcpy(clone, buf); + strncpy(clone, buf, strlen(buf)-1); // nosemgrep mutex_lock(&cpld_data->cpld_lock); tok = strsep((char**)&pclone, " "); diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/led_driver/pddf_custom_led_module.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/led_driver/pddf_custom_led_module.c index 26541eaa6109..7e88079873bf 100644 --- a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/led_driver/pddf_custom_led_module.c +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/led_driver/pddf_custom_led_module.c @@ -201,12 +201,12 @@ ssize_t get_status_led(struct device_attribute *da) return sys_val; } - strcpy(temp_data.cur_state.color, "None"); + strncpy(temp_data.cur_state.color, "None", 4); // nosemgrep for (state=0; statedata[state].bits.mask_bits); for (j = 0; j < VALUE_SIZE && ops_ptr->data[state].reg_values[j] != 0xff; j++) { if ((color_val ^ (ops_ptr->data[state].reg_values[j] << ops_ptr->data[state].bits.pos)) == 0) { - strcpy(temp_data.cur_state.color, LED_STATUS_STR[state]); + strcpy(temp_data.cur_state.color, LED_STATUS_STR[state]); // nosemgrep break; } } @@ -337,7 +337,7 @@ ssize_t store_pddf_data(struct device *dev, struct device_attribute *da, const c switch (ptr->type) { case PDDF_CHAR: - strncpy(ptr->addr, buf, strlen(buf)-1); // to discard newline char form buf + strncpy(ptr->addr, buf, strlen(buf)-1); // nosemgrep // to discard newline char form buf ptr->addr[strlen(buf)-1] = '\0'; #if DEBUG pddf_dbg(LED, KERN_ERR "[ WRITE ] ATTR PTR [%s] PDDF_CHAR VALUE:%s\n", @@ -642,7 +642,7 @@ ssize_t store_bits_data(struct device *dev, struct device_attribute *da, const c char bits[NAME_SIZE]; struct pddf_data_attribute *ptr = (struct pddf_data_attribute *)da; MASK_BITS* bits_ptr=(MASK_BITS*)(ptr->addr); - strncpy(bits_ptr->bits, buf, strlen(buf)-1); // to discard newline char form buf + strncpy(bits_ptr->bits, buf, strlen(buf)-1); // nosemgrep // to discard newline char form buf bits_ptr->bits[strlen(buf)-1] = '\0'; if((pptr=strstr(buf,":")) != NULL) { len = pptr-buf; diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/pddf_custom_fpga_extend.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/pddf_custom_fpga_extend.c index ecb099829297..2840e6a87f05 100644 --- a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/pddf_custom_fpga_extend.c +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/pddf_custom_fpga_extend.c @@ -137,7 +137,7 @@ static ssize_t set_fpga_reg_value(struct device *dev, struct device_attribute *d char *last; struct fpga_priv *fpga = dev_get_drvdata(dev); - strcpy(clone, buf); + strncpy(clone, buf, strlen(buf)-1); // nosemgrep mutex_lock(&fpga->fpga_lock); tok = strsep((char**)&pclone, " "); if (tok == NULL) { diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_api.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_api.c index 5b7897285571..1e1dcdba80e5 100644 --- a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_api.c +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_api.c @@ -49,19 +49,19 @@ void get_psu_duplicate_sysfs(int idx, char *str) switch (idx) { case PSU_V_OUT: - strcpy(str, "in3_input"); + strcpy(str, "in3_input"); // nosemgrep break; case PSU_I_OUT: - strcpy(str, "curr2_input"); + strcpy(str, "curr2_input"); // nosemgrep break; case PSU_P_OUT: - strcpy(str, "power2_input"); + strcpy(str, "power2_input"); // nosemgrep break; case PSU_FAN1_SPEED: - strcpy(str, "fan1_input"); + strcpy(str, "fan1_input"); // nosemgrep break; case PSU_TEMP1_INPUT: - strcpy(str, "temp1_input"); + strcpy(str, "temp1_input"); // nosemgrep break; default: break; @@ -235,7 +235,7 @@ ssize_t psu_show_default(struct device *dev, struct device_attribute *da, char * { sysfs_attr_info = &data->attr_info[i]; usr_data = &pdata->psu_attrs[i]; - strcpy(new_str, ""); + strcpy(new_str, ""); // nosemgrep } } @@ -438,9 +438,9 @@ int sonic_i2c_get_psu_block_default(void *client, PSU_DATA_ATTR *adata, void *da } if (strncmp(adata->devtype, "pmbus", strlen("pmbus")) == 0) - strncpy(padata->val.strval, buf+1, data_len-1); + strncpy(padata->val.strval, buf+1, data_len-1); // nosemgrep else - strncpy(padata->val.strval, buf, data_len); + strncpy(padata->val.strval, buf, data_len); // nosemgrep psu_dbg(KERN_ERR "%s: status = %d, buf block: %s\n", __FUNCTION__, status, padata->val.strval); return 0; diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_driver.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_driver.c index 1d0fb94f5ffe..27b99376304e 100644 --- a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_driver.c +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/psu_driver/pddf_psu_driver.c @@ -206,14 +206,14 @@ static int psu_probe(struct i2c_client *client, dy_ptr = (struct sensor_device_attribute *)kzalloc(sizeof(struct sensor_device_attribute)+ATTR_NAME_LEN, GFP_KERNEL); dy_ptr->dev_attr.attr.name = (char *)&dy_ptr[1]; - strcpy((char *)dy_ptr->dev_attr.attr.name, data_attr->aname); + strcpy((char *)dy_ptr->dev_attr.attr.name, data_attr->aname); // nosemgrep dy_ptr->dev_attr.attr.mode = sysfs_data_entry->a_ptr->mode; dy_ptr->dev_attr.show = sysfs_data_entry->a_ptr->show; dy_ptr->dev_attr.store = sysfs_data_entry->a_ptr->store; dy_ptr->index = sysfs_data_entry->a_ptr->index; data->psu_attribute_list[i] = &dy_ptr->dev_attr.attr; - strcpy(data->attr_info[i].name, data_attr->aname); + strcpy(data->attr_info[i].name, data_attr->aname); // nosemgrep data->attr_info[i].valid = 0; mutex_init(&data->attr_info[i].update_lock); @@ -223,7 +223,7 @@ static int psu_probe(struct i2c_client *client, { dy_ptr = (struct sensor_device_attribute *)kzalloc(sizeof(struct sensor_device_attribute)+ATTR_NAME_LEN, GFP_KERNEL); dy_ptr->dev_attr.attr.name = (char *)&dy_ptr[1]; - strcpy((char *)dy_ptr->dev_attr.attr.name, new_str); + strcpy((char *)dy_ptr->dev_attr.attr.name, new_str); // nosemgrep dy_ptr->dev_attr.attr.mode = sysfs_data_entry->a_ptr->mode; dy_ptr->dev_attr.show = sysfs_data_entry->a_ptr->show; dy_ptr->dev_attr.store = sysfs_data_entry->a_ptr->store; @@ -231,7 +231,7 @@ static int psu_probe(struct i2c_client *client, data->psu_attribute_list[num+j] = &dy_ptr->dev_attr.attr; j++; - strcpy(new_str,""); + strcpy(new_str,""); // nosemgrep } } data->psu_attribute_list[i+j] = NULL; diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/switchboard_fpga.c b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/switchboard_fpga.c index 305253403625..8b3ce30d8cc2 100644 --- a/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/switchboard_fpga.c +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/modules/switchboard_fpga.c @@ -456,7 +456,7 @@ static ssize_t set_fpga_reg_value(struct device *dev, char *pclone = clone; ssize_t status; - strcpy(clone, buf); + strncpy(clone, buf, strlen(buf)-1); // nosemgrep mutex_lock(&fpga_data->fpga_lock); tok = strsep((char**)&pclone, " "); @@ -618,7 +618,7 @@ static ssize_t cpld1_setreg_store(struct device *dev, ssize_t status; int err; - strcpy(clone, buf); + strncpy(clone, buf, strlen(buf)-1); // nosemgrep tok = strsep((char**)&pclone, " "); if (tok == NULL) { @@ -742,7 +742,7 @@ static ssize_t cpld2_setreg_store(struct device *dev, ssize_t status; int err; - strcpy(clone, buf); + strncpy(clone, buf, strlen(buf)-1); // nosemgrep tok = strsep((char**)&pclone, " "); if (tok == NULL) { @@ -1878,7 +1878,7 @@ static struct i2c_adapter * ds3000_i2c_init(struct platform_device *pdev, 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); + strcpy(new_data->pca9548.calling_name, fpga_i2c_bus_dev[portid].calling_name); // nosemgrep snprintf(new_adapter->name, sizeof(new_adapter->name), "SMBus I2C Adapter PortID: %s", new_data->pca9548.calling_name); diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/custom_component.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/custom_component.py index 67f234ef6946..f342c2cf8058 100644 --- a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/custom_component.py +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/custom_component.py @@ -188,8 +188,8 @@ def run_command(self, cmd): status = True result = "" try: - p = subprocess.Popen( - cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen( # nosemgrep + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # nosemgrep raw_data, err = p.communicate() if err.decode('UTF-8') == '': result = raw_data.strip().decode('UTF-8') diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/fan.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/fan.py index 38039c03a210..1551e554b8b4 100644 --- a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/fan.py +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/fan.py @@ -72,9 +72,9 @@ def get_target_speed(self): else: speed_rpm = self.get_speed_rpm() if self.fan_index == 1: - max_fan_rpm = eval(self.plugin_data['FAN']['FRONT_FAN_MAX_RPM_SPEED']) + max_fan_rpm = eval(self.plugin_data['FAN']['FRONT_FAN_MAX_RPM_SPEED']) # nosemgrep else: - max_fan_rpm = eval(self.plugin_data['FAN']['REAR_FAN_MAX_RPM_SPEED']) + max_fan_rpm = eval(self.plugin_data['FAN']['REAR_FAN_MAX_RPM_SPEED']) # nosemgrep speed_percentage = round(int((speed_rpm * 100) / max_fan_rpm)) target_speed = speed_percentage @@ -150,7 +150,7 @@ def set_speed(self, speed): print("Setting fan speed is not allowed !") return False - duty_cycle_to_pwm = eval(self.plugin_data['FAN']['duty_cycle_to_pwm']) + duty_cycle_to_pwm = eval(self.plugin_data['FAN']['duty_cycle_to_pwm']) # nosemgrep pwm = int(round(duty_cycle_to_pwm(speed))) if self._api_helper.is_bmc_present(): diff --git a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/helper.py b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/helper.py index 25b5d5489a5c..e03e863a7be2 100644 --- a/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/helper.py +++ b/platform/broadcom/sonic-platform-modules-cel/ds3000/pddf/sonic_platform/helper.py @@ -22,8 +22,8 @@ def pci_get_value(self, resource, offset): def get_cmd_output(self, cmd): try: - data = subprocess.check_output(cmd, shell=True, - universal_newlines=True, stderr=subprocess.STDOUT).strip() + data = subprocess.check_output(cmd, shell=True, # nosemgrep + universal_newlines=True, stderr=subprocess.STDOUT).strip() # nosemgrep status = 0 except subprocess.CalledProcessError as ex: data = ex.output